activerecord 7.0.8 → 7.1.0

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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1401 -1513
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -16
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +15 -9
  15. data/lib/active_record/associations/collection_proxy.rb +15 -10
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader.rb +13 -10
  23. data/lib/active_record/associations/singular_association.rb +1 -1
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +295 -199
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +55 -9
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -108
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -40
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
  73. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  74. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  75. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  76. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +347 -54
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +337 -176
  79. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  80. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  81. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
  82. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  83. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  84. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  85. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
  86. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  87. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  88. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  89. data/lib/active_record/connection_adapters.rb +3 -1
  90. data/lib/active_record/connection_handling.rb +71 -94
  91. data/lib/active_record/core.rb +134 -146
  92. data/lib/active_record/counter_cache.rb +46 -25
  93. data/lib/active_record/database_configurations/database_config.rb +9 -3
  94. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  95. data/lib/active_record/database_configurations/url_config.rb +17 -11
  96. data/lib/active_record/database_configurations.rb +86 -33
  97. data/lib/active_record/delegated_type.rb +8 -3
  98. data/lib/active_record/deprecator.rb +7 -0
  99. data/lib/active_record/destroy_association_async_job.rb +2 -0
  100. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  102. data/lib/active_record/encryption/config.rb +25 -1
  103. data/lib/active_record/encryption/configurable.rb +12 -19
  104. data/lib/active_record/encryption/context.rb +10 -3
  105. data/lib/active_record/encryption/contexts.rb +5 -1
  106. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  107. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  108. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  109. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
  110. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  111. data/lib/active_record/encryption/key_generator.rb +12 -1
  112. data/lib/active_record/encryption/message_serializer.rb +2 -0
  113. data/lib/active_record/encryption/properties.rb +3 -3
  114. data/lib/active_record/encryption/scheme.rb +19 -22
  115. data/lib/active_record/encryption.rb +1 -0
  116. data/lib/active_record/enum.rb +113 -26
  117. data/lib/active_record/errors.rb +108 -15
  118. data/lib/active_record/explain.rb +23 -3
  119. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  120. data/lib/active_record/fixture_set/render_context.rb +2 -0
  121. data/lib/active_record/fixture_set/table_row.rb +29 -8
  122. data/lib/active_record/fixtures.rb +119 -71
  123. data/lib/active_record/future_result.rb +30 -5
  124. data/lib/active_record/gem_version.rb +3 -3
  125. data/lib/active_record/inheritance.rb +30 -16
  126. data/lib/active_record/insert_all.rb +55 -8
  127. data/lib/active_record/integration.rb +8 -8
  128. data/lib/active_record/internal_metadata.rb +118 -30
  129. data/lib/active_record/locking/pessimistic.rb +5 -2
  130. data/lib/active_record/log_subscriber.rb +29 -12
  131. data/lib/active_record/marshalling.rb +56 -0
  132. data/lib/active_record/message_pack.rb +124 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  134. data/lib/active_record/middleware/database_selector.rb +5 -7
  135. data/lib/active_record/middleware/shard_selector.rb +3 -1
  136. data/lib/active_record/migration/command_recorder.rb +100 -4
  137. data/lib/active_record/migration/compatibility.rb +131 -5
  138. data/lib/active_record/migration/default_strategy.rb +23 -0
  139. data/lib/active_record/migration/execution_strategy.rb +19 -0
  140. data/lib/active_record/migration.rb +213 -109
  141. data/lib/active_record/model_schema.rb +60 -40
  142. data/lib/active_record/nested_attributes.rb +23 -3
  143. data/lib/active_record/normalization.rb +159 -0
  144. data/lib/active_record/persistence.rb +184 -34
  145. data/lib/active_record/promise.rb +84 -0
  146. data/lib/active_record/query_cache.rb +3 -21
  147. data/lib/active_record/query_logs.rb +77 -52
  148. data/lib/active_record/query_logs_formatter.rb +41 -0
  149. data/lib/active_record/querying.rb +15 -2
  150. data/lib/active_record/railtie.rb +108 -46
  151. data/lib/active_record/railties/controller_runtime.rb +10 -5
  152. data/lib/active_record/railties/databases.rake +139 -145
  153. data/lib/active_record/railties/job_runtime.rb +23 -0
  154. data/lib/active_record/readonly_attributes.rb +32 -5
  155. data/lib/active_record/reflection.rb +162 -44
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  157. data/lib/active_record/relation/batches.rb +190 -61
  158. data/lib/active_record/relation/calculations.rb +152 -63
  159. data/lib/active_record/relation/delegation.rb +22 -8
  160. data/lib/active_record/relation/finder_methods.rb +77 -16
  161. data/lib/active_record/relation/merger.rb +2 -0
  162. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  163. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  164. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  165. data/lib/active_record/relation/predicate_builder.rb +26 -14
  166. data/lib/active_record/relation/query_attribute.rb +2 -1
  167. data/lib/active_record/relation/query_methods.rb +351 -62
  168. data/lib/active_record/relation/spawn_methods.rb +18 -1
  169. data/lib/active_record/relation.rb +76 -35
  170. data/lib/active_record/result.rb +19 -5
  171. data/lib/active_record/runtime_registry.rb +10 -1
  172. data/lib/active_record/sanitization.rb +51 -11
  173. data/lib/active_record/schema.rb +2 -3
  174. data/lib/active_record/schema_dumper.rb +46 -7
  175. data/lib/active_record/schema_migration.rb +68 -33
  176. data/lib/active_record/scoping/default.rb +15 -5
  177. data/lib/active_record/scoping/named.rb +2 -2
  178. data/lib/active_record/scoping.rb +2 -1
  179. data/lib/active_record/secure_password.rb +60 -0
  180. data/lib/active_record/secure_token.rb +21 -3
  181. data/lib/active_record/signed_id.rb +7 -5
  182. data/lib/active_record/store.rb +8 -8
  183. data/lib/active_record/suppressor.rb +3 -1
  184. data/lib/active_record/table_metadata.rb +10 -1
  185. data/lib/active_record/tasks/database_tasks.rb +127 -105
  186. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  187. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  188. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  189. data/lib/active_record/test_fixtures.rb +113 -96
  190. data/lib/active_record/timestamp.rb +26 -14
  191. data/lib/active_record/token_for.rb +113 -0
  192. data/lib/active_record/touch_later.rb +11 -6
  193. data/lib/active_record/transactions.rb +36 -10
  194. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  195. data/lib/active_record/type/internal/timezone.rb +7 -2
  196. data/lib/active_record/type/time.rb +4 -0
  197. data/lib/active_record/validations/absence.rb +1 -1
  198. data/lib/active_record/validations/numericality.rb +5 -4
  199. data/lib/active_record/validations/presence.rb +5 -28
  200. data/lib/active_record/validations/uniqueness.rb +47 -2
  201. data/lib/active_record/validations.rb +8 -4
  202. data/lib/active_record/version.rb +1 -1
  203. data/lib/active_record.rb +121 -16
  204. data/lib/arel/errors.rb +10 -0
  205. data/lib/arel/factory_methods.rb +4 -0
  206. data/lib/arel/nodes/binary.rb +6 -1
  207. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  208. data/lib/arel/nodes/cte.rb +36 -0
  209. data/lib/arel/nodes/fragments.rb +35 -0
  210. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  211. data/lib/arel/nodes/leading_join.rb +8 -0
  212. data/lib/arel/nodes/node.rb +111 -2
  213. data/lib/arel/nodes/sql_literal.rb +6 -0
  214. data/lib/arel/nodes/table_alias.rb +4 -0
  215. data/lib/arel/nodes.rb +4 -0
  216. data/lib/arel/predications.rb +2 -0
  217. data/lib/arel/table.rb +9 -5
  218. data/lib/arel/visitors/mysql.rb +8 -1
  219. data/lib/arel/visitors/to_sql.rb +81 -17
  220. data/lib/arel/visitors/visitor.rb +2 -2
  221. data/lib/arel.rb +16 -2
  222. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  223. data/lib/rails/generators/active_record/migration.rb +3 -1
  224. data/lib/rails/generators/active_record/model/USAGE +113 -0
  225. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  226. metadata +46 -11
  227. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  228. data/lib/active_record/null_relation.rb +0 -63
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Trilogy
6
+ module DatabaseStatements
7
+ def select_all(*, **) # :nodoc:
8
+ result = super
9
+ with_raw_connection do |conn|
10
+ conn.next_result while conn.more_results_exist?
11
+ end
12
+ result
13
+ end
14
+
15
+ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
16
+ sql = transform_query(sql)
17
+ check_if_write_query(sql)
18
+ mark_transaction_written_if_write(sql)
19
+
20
+ result = raw_execute(sql, name, async: async)
21
+ ActiveRecord::Result.new(result.fields, result.to_a)
22
+ end
23
+
24
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil, returning: nil) # :nodoc:
25
+ sql = transform_query(sql)
26
+ check_if_write_query(sql)
27
+ mark_transaction_written_if_write(sql)
28
+
29
+ raw_execute(to_sql(sql, binds), name)
30
+ end
31
+
32
+ def exec_delete(sql, name = nil, binds = []) # :nodoc:
33
+ sql = transform_query(sql)
34
+ check_if_write_query(sql)
35
+ mark_transaction_written_if_write(sql)
36
+
37
+ result = raw_execute(to_sql(sql, binds), name)
38
+ result.affected_rows
39
+ end
40
+
41
+ alias :exec_update :exec_delete # :nodoc:
42
+
43
+ private
44
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
45
+ log(sql, name, async: async) do
46
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
47
+ sync_timezone_changes(conn)
48
+ result = conn.query(sql)
49
+ handle_warnings(sql)
50
+ result
51
+ end
52
+ end
53
+ end
54
+
55
+ def last_inserted_id(result)
56
+ result.last_insert_id
57
+ end
58
+
59
+ def sync_timezone_changes(conn)
60
+ # Sync any changes since connection last established.
61
+ if default_timezone == :local
62
+ conn.query_flags |= ::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
63
+ else
64
+ conn.query_flags &= ~::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
65
+ end
66
+ end
67
+
68
+ def execute_batch(statements, name = nil)
69
+ statements = statements.map { |sql| transform_query(sql) }
70
+ combine_multi_statements(statements).each do |statement|
71
+ with_raw_connection do |conn|
72
+ raw_execute(statement, name)
73
+ conn.next_result while conn.more_results_exist?
74
+ end
75
+ end
76
+ end
77
+
78
+ def multi_statements_enabled?
79
+ !!@config[:multi_statement]
80
+ end
81
+
82
+ def with_multi_statements
83
+ if multi_statements_enabled?
84
+ return yield
85
+ end
86
+
87
+ with_raw_connection do |conn|
88
+ conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_ON)
89
+
90
+ yield
91
+ ensure
92
+ conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,254 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/connection_adapters/abstract_mysql_adapter"
4
+
5
+ gem "trilogy", "~> 2.4"
6
+ require "trilogy"
7
+
8
+ require "active_record/connection_adapters/trilogy/database_statements"
9
+
10
+ module ActiveRecord
11
+ module ConnectionHandling # :nodoc:
12
+ def trilogy_adapter_class
13
+ ConnectionAdapters::TrilogyAdapter
14
+ end
15
+
16
+ # Establishes a connection to the database that's used by all Active Record objects.
17
+ def trilogy_connection(config)
18
+ configuration = config.dup
19
+
20
+ # Set FOUND_ROWS capability on the connection so UPDATE queries returns number of rows
21
+ # matched rather than number of rows updated.
22
+ configuration[:found_rows] = true
23
+
24
+ options = [
25
+ configuration[:host],
26
+ configuration[:port],
27
+ configuration[:database],
28
+ configuration[:username],
29
+ configuration[:password],
30
+ configuration[:socket],
31
+ 0
32
+ ]
33
+
34
+ trilogy_adapter_class.new nil, logger, options, configuration
35
+ end
36
+ end
37
+ module ConnectionAdapters
38
+ class TrilogyAdapter < AbstractMysqlAdapter
39
+ ER_BAD_DB_ERROR = 1049
40
+ ER_DBACCESS_DENIED_ERROR = 1044
41
+ ER_ACCESS_DENIED_ERROR = 1045
42
+ ER_SERVER_SHUTDOWN = 1053
43
+
44
+ ADAPTER_NAME = "Trilogy"
45
+
46
+ include Trilogy::DatabaseStatements
47
+
48
+ SSL_MODES = {
49
+ SSL_MODE_DISABLED: ::Trilogy::SSL_DISABLED,
50
+ SSL_MODE_PREFERRED: ::Trilogy::SSL_PREFERRED_NOVERIFY,
51
+ SSL_MODE_REQUIRED: ::Trilogy::SSL_REQUIRED_NOVERIFY,
52
+ SSL_MODE_VERIFY_CA: ::Trilogy::SSL_VERIFY_CA,
53
+ SSL_MODE_VERIFY_IDENTITY: ::Trilogy::SSL_VERIFY_IDENTITY
54
+ }.freeze
55
+
56
+ class << self
57
+ def new_client(config)
58
+ config[:ssl_mode] = parse_ssl_mode(config[:ssl_mode]) if config[:ssl_mode]
59
+ ::Trilogy.new(config)
60
+ rescue ::Trilogy::ConnectionError, ::Trilogy::ProtocolError => error
61
+ raise translate_connect_error(config, error)
62
+ end
63
+
64
+ def parse_ssl_mode(mode)
65
+ return mode if mode.is_a? Integer
66
+
67
+ m = mode.to_s.upcase
68
+ m = "SSL_MODE_#{m}" unless m.start_with? "SSL_MODE_"
69
+
70
+ SSL_MODES.fetch(m.to_sym, mode)
71
+ end
72
+
73
+ def translate_connect_error(config, error)
74
+ case error.error_code
75
+ when ER_DBACCESS_DENIED_ERROR, ER_BAD_DB_ERROR
76
+ ActiveRecord::NoDatabaseError.db_error(config[:database])
77
+ when ER_ACCESS_DENIED_ERROR
78
+ ActiveRecord::DatabaseConnectionError.username_error(config[:username])
79
+ else
80
+ if error.message.include?("TRILOGY_DNS_ERROR")
81
+ ActiveRecord::DatabaseConnectionError.hostname_error(config[:host])
82
+ else
83
+ ActiveRecord::ConnectionNotEstablished.new(error.message)
84
+ end
85
+ end
86
+ end
87
+
88
+ private
89
+ def initialize_type_map(m)
90
+ super
91
+
92
+ m.register_type(%r(char)i) do |sql_type|
93
+ limit = extract_limit(sql_type)
94
+ Type.lookup(:string, adapter: :trilogy, limit: limit)
95
+ end
96
+
97
+ m.register_type %r(^enum)i, Type.lookup(:string, adapter: :trilogy)
98
+ m.register_type %r(^set)i, Type.lookup(:string, adapter: :trilogy)
99
+ end
100
+ end
101
+
102
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
103
+
104
+ def supports_json?
105
+ !mariadb? && database_version >= "5.7.8"
106
+ end
107
+
108
+ def supports_comments?
109
+ true
110
+ end
111
+
112
+ def supports_comments_in_create?
113
+ true
114
+ end
115
+
116
+ def supports_savepoints?
117
+ true
118
+ end
119
+
120
+ def savepoint_errors_invalidate_transactions?
121
+ true
122
+ end
123
+
124
+ def supports_lazy_transactions?
125
+ true
126
+ end
127
+
128
+ def quote_string(string)
129
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
130
+ conn.escape(string)
131
+ end
132
+ end
133
+
134
+ def active?
135
+ connection&.ping || false
136
+ rescue ::Trilogy::Error
137
+ false
138
+ end
139
+
140
+ alias reset! reconnect!
141
+
142
+ def disconnect!
143
+ super
144
+ unless connection.nil?
145
+ connection.close
146
+ self.connection = nil
147
+ end
148
+ end
149
+
150
+ def discard!
151
+ super
152
+ unless connection.nil?
153
+ connection.discard!
154
+ self.connection = nil
155
+ end
156
+ end
157
+
158
+ private
159
+ def text_type?(type)
160
+ TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
161
+ end
162
+
163
+ def each_hash(result)
164
+ return to_enum(:each_hash, result) unless block_given?
165
+
166
+ keys = result.fields.map(&:to_sym)
167
+ result.rows.each do |row|
168
+ hash = {}
169
+ idx = 0
170
+ row.each do |value|
171
+ hash[keys[idx]] = value
172
+ idx += 1
173
+ end
174
+ yield hash
175
+ end
176
+
177
+ nil
178
+ end
179
+
180
+ def error_number(exception)
181
+ exception.error_code if exception.respond_to?(:error_code)
182
+ end
183
+
184
+ def connection
185
+ @raw_connection
186
+ end
187
+
188
+ def connection=(conn)
189
+ @raw_connection = conn
190
+ end
191
+
192
+ def connect
193
+ self.connection = self.class.new_client(@config)
194
+ rescue ConnectionNotEstablished => ex
195
+ raise ex.set_pool(@pool)
196
+ end
197
+
198
+ def reconnect
199
+ connection&.close
200
+ self.connection = nil
201
+ connect
202
+ end
203
+
204
+ def full_version
205
+ schema_cache.database_version.full_version_string
206
+ end
207
+
208
+ def get_full_version
209
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
210
+ conn.server_info[:version]
211
+ end
212
+ end
213
+
214
+ def translate_exception(exception, message:, sql:, binds:)
215
+ if exception.is_a?(::Trilogy::TimeoutError) && !exception.error_code
216
+ return ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
217
+ end
218
+ error_code = exception.error_code if exception.respond_to?(:error_code)
219
+
220
+ case error_code
221
+ when ER_SERVER_SHUTDOWN
222
+ return ConnectionFailed.new(message, connection_pool: @pool)
223
+ end
224
+
225
+ case exception
226
+ when Errno::EPIPE, SocketError, IOError
227
+ return ConnectionFailed.new(message, connection_pool: @pool)
228
+ when ::Trilogy::Error
229
+ if /Connection reset by peer|TRILOGY_CLOSED_CONNECTION|TRILOGY_INVALID_SEQUENCE_ID|TRILOGY_UNEXPECTED_PACKET/.match?(exception.message)
230
+ return ConnectionFailed.new(message, connection_pool: @pool)
231
+ end
232
+ end
233
+
234
+ super
235
+ end
236
+
237
+ def default_prepared_statements
238
+ false
239
+ end
240
+
241
+ ActiveRecord::Type.register(:immutable_string, adapter: :trilogy) do |_, **args|
242
+ Type::ImmutableString.new(true: "1", false: "0", **args)
243
+ end
244
+
245
+ ActiveRecord::Type.register(:string, adapter: :trilogy) do |_, **args|
246
+ Type::String.new(true: "1", false: "0", **args)
247
+ end
248
+
249
+ ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :trilogy)
250
+ end
251
+
252
+ ActiveSupport.run_load_hooks(:active_record_trilogyadapter, TrilogyAdapter)
253
+ end
254
+ end
@@ -11,14 +11,16 @@ module ActiveRecord
11
11
  autoload :Column
12
12
  autoload :PoolConfig
13
13
  autoload :PoolManager
14
- autoload :LegacyPoolManager
15
14
  autoload :SchemaCache
15
+ autoload :BoundSchemaReflection, "active_record/connection_adapters/schema_cache"
16
+ autoload :SchemaReflection, "active_record/connection_adapters/schema_cache"
16
17
  autoload :Deduplicable
17
18
 
18
19
  autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
19
20
  autoload :IndexDefinition
20
21
  autoload :ColumnDefinition
21
22
  autoload :ChangeColumnDefinition
23
+ autoload :ChangeColumnDefaultDefinition
22
24
  autoload :ForeignKeyDefinition
23
25
  autoload :CheckConstraintDefinition
24
26
  autoload :TableDefinition
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
+ # = Active Record Connection Handling
4
5
  module ConnectionHandling
5
6
  RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence }
6
7
  DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
@@ -38,7 +39,7 @@ module ActiveRecord
38
39
  # )
39
40
  #
40
41
  # In case {ActiveRecord::Base.configurations}[rdoc-ref:Core.configurations]
41
- # is set (Rails automatically loads the contents of config/database.yml into it),
42
+ # is set (\Rails automatically loads the contents of config/database.yml into it),
42
43
  # a symbol can also be given as argument, representing a key in the
43
44
  # configuration hash:
44
45
  #
@@ -48,16 +49,15 @@ module ActiveRecord
48
49
  # may be returned on an error.
49
50
  def establish_connection(config_or_env = nil)
50
51
  config_or_env ||= DEFAULT_ENV.call.to_sym
51
- db_config, owner_name = resolve_config_for_connection(config_or_env)
52
- connection_handler.establish_connection(db_config, owner_name: owner_name, role: current_role, shard: current_shard)
52
+ db_config = resolve_config_for_connection(config_or_env)
53
+ connection_handler.establish_connection(db_config, owner_name: self, role: current_role, shard: current_shard)
53
54
  end
54
55
 
55
56
  # Connects a model to the databases specified. The +database+ keyword
56
57
  # takes a hash consisting of a +role+ and a +database_key+.
57
58
  #
58
- # This will create a connection handler for switching between connections,
59
- # look up the config hash using the +database_key+ and finally
60
- # establishes a connection to that config.
59
+ # This will look up the database config using the +database_key+ and
60
+ # establish a connection to that config.
61
61
  #
62
62
  # class AnimalsModel < ApplicationRecord
63
63
  # self.abstract_class = true
@@ -66,7 +66,7 @@ module ActiveRecord
66
66
  # end
67
67
  #
68
68
  # +connects_to+ also supports horizontal sharding. The horizontal sharding API
69
- # also supports read replicas. Connect a model to a list of shards like this:
69
+ # supports read replicas as well. You can connect a model to a list of shards like this:
70
70
  #
71
71
  # class AnimalsModel < ApplicationRecord
72
72
  # self.abstract_class = true
@@ -87,21 +87,18 @@ module ActiveRecord
87
87
 
88
88
  connections = []
89
89
 
90
- database.each do |role, database_key|
91
- db_config, owner_name = resolve_config_for_connection(database_key)
92
- handler = lookup_connection_handler(role.to_sym)
93
-
94
- self.connection_class = true
95
- connections << handler.establish_connection(db_config, owner_name: owner_name, role: role)
90
+ if shards.empty?
91
+ shards[:default] = database
96
92
  end
97
93
 
94
+ self.default_shard = shards.keys.first
95
+
98
96
  shards.each do |shard, database_keys|
99
97
  database_keys.each do |role, database_key|
100
- db_config, owner_name = resolve_config_for_connection(database_key)
101
- handler = lookup_connection_handler(role.to_sym)
98
+ db_config = resolve_config_for_connection(database_key)
102
99
 
103
100
  self.connection_class = true
104
- connections << handler.establish_connection(db_config, owner_name: owner_name, role: role, shard: shard.to_sym)
101
+ connections << connection_handler.establish_connection(db_config, owner_name: self, role: role, shard: shard.to_sym)
105
102
  end
106
103
  end
107
104
 
@@ -135,18 +132,12 @@ module ActiveRecord
135
132
  # Dog.first # finds first Dog record stored on the shard one replica
136
133
  # end
137
134
  def connected_to(role: nil, shard: nil, prevent_writes: false, &blk)
138
- if ActiveRecord.legacy_connection_handling
139
- if self != Base
140
- raise NotImplementedError, "`connected_to` can only be called on ActiveRecord::Base with legacy connection handling."
141
- end
142
- else
143
- if self != Base && !abstract_class
144
- raise NotImplementedError, "calling `connected_to` is only allowed on ActiveRecord::Base or abstract classes."
145
- end
135
+ if self != Base && !abstract_class
136
+ raise NotImplementedError, "calling `connected_to` is only allowed on ActiveRecord::Base or abstract classes."
137
+ end
146
138
 
147
- if name != connection_specification_name && !primary_class?
148
- raise NotImplementedError, "calling `connected_to` is only allowed on the abstract class that established the connection."
149
- end
139
+ if !connection_class? && !primary_class?
140
+ raise NotImplementedError, "calling `connected_to` is only allowed on the abstract class that established the connection."
150
141
  end
151
142
 
152
143
  unless role || shard
@@ -172,10 +163,6 @@ module ActiveRecord
172
163
  def connected_to_many(*classes, role:, shard: nil, prevent_writes: false)
173
164
  classes = classes.flatten
174
165
 
175
- if ActiveRecord.legacy_connection_handling
176
- raise NotImplementedError, "connected_to_many is not available with legacy connection handling"
177
- end
178
-
179
166
  if self != Base || classes.include?(Base)
180
167
  raise NotImplementedError, "connected_to_many can only be called on ActiveRecord::Base."
181
168
  end
@@ -196,10 +183,6 @@ module ActiveRecord
196
183
  # It is not recommended to use this method in a request since it
197
184
  # does not yield to a block like +connected_to+.
198
185
  def connecting_to(role: default_role, shard: default_shard, prevent_writes: false)
199
- if ActiveRecord.legacy_connection_handling
200
- raise NotImplementedError, "`connecting_to` is not available with `legacy_connection_handling`."
201
- end
202
-
203
186
  prevent_writes = true if role == ActiveRecord.reading_role
204
187
 
205
188
  append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
@@ -236,40 +219,31 @@ module ActiveRecord
236
219
  # See +READ_QUERY+ for the queries that are blocked by this
237
220
  # method.
238
221
  def while_preventing_writes(enabled = true, &block)
239
- if ActiveRecord.legacy_connection_handling
240
- connection_handler.while_preventing_writes(enabled, &block)
241
- else
242
- connected_to(role: current_role, prevent_writes: enabled, &block)
243
- end
222
+ connected_to(role: current_role, prevent_writes: enabled, &block)
244
223
  end
245
224
 
246
- # Returns true if role is the current connected role.
225
+ # Returns true if role is the current connected role and/or
226
+ # current connected shard. If no shard is passed, the default will be
227
+ # used.
247
228
  #
248
229
  # ActiveRecord::Base.connected_to(role: :writing) do
249
230
  # ActiveRecord::Base.connected_to?(role: :writing) #=> true
250
231
  # ActiveRecord::Base.connected_to?(role: :reading) #=> false
251
232
  # end
233
+ #
234
+ # ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one) do
235
+ # ActiveRecord::Base.connected_to?(role: :reading, shard: :shard_one) #=> true
236
+ # ActiveRecord::Base.connected_to?(role: :reading, shard: :default) #=> false
237
+ # ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one) #=> true
238
+ # end
252
239
  def connected_to?(role:, shard: ActiveRecord::Base.default_shard)
253
240
  current_role == role.to_sym && current_shard == shard.to_sym
254
241
  end
255
242
 
256
- def lookup_connection_handler(handler_key) # :nodoc:
257
- if ActiveRecord.legacy_connection_handling
258
- handler_key ||= ActiveRecord.writing_role
259
- connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
260
- else
261
- ActiveRecord::Base.connection_handler
262
- end
263
- end
264
-
265
243
  # Clears the query cache for all connections associated with the current thread.
266
244
  def clear_query_caches_for_current_thread
267
- if ActiveRecord.legacy_connection_handling
268
- ActiveRecord::Base.connection_handlers.each_value do |handler|
269
- clear_on_handler(handler)
270
- end
271
- else
272
- clear_on_handler(ActiveRecord::Base.connection_handler)
245
+ connection_handler.each_connection_pool do |pool|
246
+ pool.connection.clear_query_cache if pool.active_connection?
273
247
  end
274
248
  end
275
249
 
@@ -298,7 +272,7 @@ module ActiveRecord
298
272
  #
299
273
  # ActiveRecord::Base.connection_db_config
300
274
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
301
- # @name="primary", @config={pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}>
275
+ # @name="primary", @config={pool: 5, timeout: 5000, database: "storage/development.sqlite3", adapter: "sqlite3"}>
302
276
  #
303
277
  # Use only for reading.
304
278
  def connection_db_config
@@ -319,6 +293,14 @@ module ActiveRecord
319
293
  end
320
294
 
321
295
  def remove_connection(name = nil)
296
+ if name
297
+ ActiveRecord.deprecator.warn(<<-MSG.squish)
298
+ The name argument for `#remove_connection` is deprecated without replacement
299
+ and will be removed in Rails 7.2. `#remove_connection` should always be called
300
+ on the connection class directly, which makes the name argument obsolete.
301
+ MSG
302
+ end
303
+
322
304
  name ||= @connection_specification_name if defined?(@connection_specification_name)
323
305
  # if removing a connection that has a pool, we reset the
324
306
  # connection_specification_name so it will use the parent
@@ -334,47 +316,51 @@ module ActiveRecord
334
316
  connection.schema_cache.clear!
335
317
  end
336
318
 
337
- delegate :clear_active_connections!, :clear_reloadable_connections!,
338
- :clear_all_connections!, :flush_idle_connections!, to: :connection_handler
319
+ def clear_active_connections!(role = nil)
320
+ deprecation_for_delegation(__method__)
321
+ connection_handler.clear_active_connections!(role)
322
+ end
323
+
324
+ def clear_reloadable_connections!(role = nil)
325
+ deprecation_for_delegation(__method__)
326
+ connection_handler.clear_reloadable_connections!(role)
327
+ end
328
+
329
+ def clear_all_connections!(role = nil)
330
+ deprecation_for_delegation(__method__)
331
+ connection_handler.clear_all_connections!(role)
332
+ end
333
+
334
+ def flush_idle_connections!(role = nil)
335
+ deprecation_for_delegation(__method__)
336
+ connection_handler.flush_idle_connections!(role)
337
+ end
339
338
 
340
339
  private
341
- def clear_on_handler(handler)
342
- handler.all_connection_pools.each do |pool|
343
- pool.connection.clear_query_cache if pool.active_connection?
344
- end
340
+ def deprecation_for_delegation(method)
341
+ ActiveRecord.deprecator.warn(<<-MSG.squish)
342
+ Calling `ActiveRecord::Base.#{method} is deprecated. Please
343
+ call the method directly on the connection handler; for
344
+ example: `ActiveRecord::Base.connection_handler.#{method}`.
345
+ MSG
345
346
  end
346
347
 
347
348
  def resolve_config_for_connection(config_or_env)
348
349
  raise "Anonymous class is not allowed." unless name
349
350
 
350
- owner_name = primary_class? ? Base.name : name
351
- self.connection_specification_name = owner_name
351
+ connection_name = primary_class? ? Base.name : name
352
+ self.connection_specification_name = connection_name
352
353
 
353
- db_config = Base.configurations.resolve(config_or_env)
354
- [db_config, self]
355
- end
356
-
357
- def with_handler(handler_key, &blk)
358
- handler = lookup_connection_handler(handler_key)
359
- swap_connection_handler(handler, &blk)
354
+ Base.configurations.resolve(config_or_env)
360
355
  end
361
356
 
362
357
  def with_role_and_shard(role, shard, prevent_writes)
363
358
  prevent_writes = true if role == ActiveRecord.reading_role
364
359
 
365
- if ActiveRecord.legacy_connection_handling
366
- with_handler(role.to_sym) do
367
- connection_handler.while_preventing_writes(prevent_writes) do
368
- append_to_connected_to_stack(shard: shard, klasses: [self])
369
- yield
370
- end
371
- end
372
- else
373
- append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
374
- return_value = yield
375
- return_value.load if return_value.is_a? ActiveRecord::Relation
376
- return_value
377
- end
360
+ append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
361
+ return_value = yield
362
+ return_value.load if return_value.is_a? ActiveRecord::Relation
363
+ return_value
378
364
  ensure
379
365
  self.connected_to_stack.pop
380
366
  end
@@ -386,14 +372,5 @@ module ActiveRecord
386
372
 
387
373
  connected_to_stack << entry
388
374
  end
389
-
390
- def swap_connection_handler(handler, &blk) # :nodoc:
391
- old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
392
- return_value = yield
393
- return_value.load if return_value.is_a? ActiveRecord::Relation
394
- return_value
395
- ensure
396
- ActiveRecord::Base.connection_handler = old_handler
397
- end
398
375
  end
399
376
  end