activerecord 6.1.3.2 → 7.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +734 -1058
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +35 -7
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +16 -6
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +24 -25
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/preloader/association.rb +161 -49
  25. data/lib/active_record/associations/preloader/batch.rb +51 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +37 -11
  28. data/lib/active_record/associations/preloader.rb +46 -110
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +1 -1
  31. data/lib/active_record/associations.rb +76 -81
  32. data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +41 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +6 -9
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +3 -18
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +11 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -18
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -69
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
  61. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  66. data/lib/active_record/connection_adapters/pool_config.rb +1 -3
  67. data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  69. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  76. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
  82. data/lib/active_record/connection_adapters/schema_cache.rb +35 -4
  83. data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
  84. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
  85. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  86. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  87. data/lib/active_record/connection_adapters.rb +8 -5
  88. data/lib/active_record/connection_handling.rb +20 -38
  89. data/lib/active_record/core.rb +129 -117
  90. data/lib/active_record/database_configurations/database_config.rb +12 -0
  91. data/lib/active_record/database_configurations/hash_config.rb +27 -1
  92. data/lib/active_record/database_configurations/url_config.rb +2 -2
  93. data/lib/active_record/database_configurations.rb +18 -9
  94. data/lib/active_record/delegated_type.rb +33 -11
  95. data/lib/active_record/destroy_association_async_job.rb +1 -1
  96. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  97. data/lib/active_record/dynamic_matchers.rb +1 -1
  98. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  99. data/lib/active_record/encryption/cipher.rb +53 -0
  100. data/lib/active_record/encryption/config.rb +44 -0
  101. data/lib/active_record/encryption/configurable.rb +61 -0
  102. data/lib/active_record/encryption/context.rb +35 -0
  103. data/lib/active_record/encryption/contexts.rb +72 -0
  104. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  105. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  106. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  107. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  108. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  109. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  110. data/lib/active_record/encryption/encryptor.rb +155 -0
  111. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  112. data/lib/active_record/encryption/errors.rb +15 -0
  113. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  114. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
  115. data/lib/active_record/encryption/key.rb +28 -0
  116. data/lib/active_record/encryption/key_generator.rb +42 -0
  117. data/lib/active_record/encryption/key_provider.rb +46 -0
  118. data/lib/active_record/encryption/message.rb +33 -0
  119. data/lib/active_record/encryption/message_serializer.rb +80 -0
  120. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  121. data/lib/active_record/encryption/properties.rb +76 -0
  122. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  123. data/lib/active_record/encryption/scheme.rb +99 -0
  124. data/lib/active_record/encryption.rb +55 -0
  125. data/lib/active_record/enum.rb +44 -46
  126. data/lib/active_record/errors.rb +66 -3
  127. data/lib/active_record/fixture_set/file.rb +15 -1
  128. data/lib/active_record/fixture_set/table_row.rb +40 -5
  129. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  130. data/lib/active_record/fixtures.rb +16 -11
  131. data/lib/active_record/future_result.rb +139 -0
  132. data/lib/active_record/gem_version.rb +4 -4
  133. data/lib/active_record/inheritance.rb +55 -17
  134. data/lib/active_record/insert_all.rb +39 -6
  135. data/lib/active_record/integration.rb +1 -1
  136. data/lib/active_record/internal_metadata.rb +3 -5
  137. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  138. data/lib/active_record/locking/optimistic.rb +10 -9
  139. data/lib/active_record/log_subscriber.rb +6 -2
  140. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  141. data/lib/active_record/middleware/database_selector.rb +8 -3
  142. data/lib/active_record/migration/command_recorder.rb +4 -4
  143. data/lib/active_record/migration/compatibility.rb +83 -1
  144. data/lib/active_record/migration/join_table.rb +1 -1
  145. data/lib/active_record/migration.rb +109 -79
  146. data/lib/active_record/model_schema.rb +46 -32
  147. data/lib/active_record/nested_attributes.rb +3 -3
  148. data/lib/active_record/no_touching.rb +2 -2
  149. data/lib/active_record/null_relation.rb +2 -6
  150. data/lib/active_record/persistence.rb +134 -45
  151. data/lib/active_record/query_cache.rb +2 -2
  152. data/lib/active_record/query_logs.rb +203 -0
  153. data/lib/active_record/querying.rb +15 -5
  154. data/lib/active_record/railtie.rb +117 -17
  155. data/lib/active_record/railties/controller_runtime.rb +1 -1
  156. data/lib/active_record/railties/databases.rake +83 -58
  157. data/lib/active_record/readonly_attributes.rb +11 -0
  158. data/lib/active_record/reflection.rb +45 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  160. data/lib/active_record/relation/batches.rb +3 -3
  161. data/lib/active_record/relation/calculations.rb +42 -25
  162. data/lib/active_record/relation/delegation.rb +6 -6
  163. data/lib/active_record/relation/finder_methods.rb +32 -23
  164. data/lib/active_record/relation/merger.rb +20 -13
  165. data/lib/active_record/relation/predicate_builder.rb +1 -6
  166. data/lib/active_record/relation/query_attribute.rb +5 -11
  167. data/lib/active_record/relation/query_methods.rb +233 -50
  168. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  169. data/lib/active_record/relation/spawn_methods.rb +2 -2
  170. data/lib/active_record/relation/where_clause.rb +22 -15
  171. data/lib/active_record/relation.rb +170 -87
  172. data/lib/active_record/result.rb +17 -2
  173. data/lib/active_record/runtime_registry.rb +2 -4
  174. data/lib/active_record/sanitization.rb +11 -7
  175. data/lib/active_record/schema_dumper.rb +3 -3
  176. data/lib/active_record/schema_migration.rb +0 -4
  177. data/lib/active_record/scoping/default.rb +62 -15
  178. data/lib/active_record/scoping/named.rb +3 -11
  179. data/lib/active_record/scoping.rb +40 -22
  180. data/lib/active_record/serialization.rb +1 -1
  181. data/lib/active_record/signed_id.rb +1 -1
  182. data/lib/active_record/statement_cache.rb +2 -2
  183. data/lib/active_record/tasks/database_tasks.rb +107 -23
  184. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  185. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
  186. data/lib/active_record/test_databases.rb +1 -1
  187. data/lib/active_record/test_fixtures.rb +45 -4
  188. data/lib/active_record/timestamp.rb +3 -4
  189. data/lib/active_record/transactions.rb +9 -14
  190. data/lib/active_record/translation.rb +2 -2
  191. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  192. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  193. data/lib/active_record/type/internal/timezone.rb +2 -2
  194. data/lib/active_record/type/serialized.rb +1 -1
  195. data/lib/active_record/type/type_map.rb +17 -20
  196. data/lib/active_record/type.rb +1 -2
  197. data/lib/active_record/validations/associated.rb +1 -1
  198. data/lib/active_record/validations/numericality.rb +1 -1
  199. data/lib/active_record.rb +170 -2
  200. data/lib/arel/attributes/attribute.rb +0 -8
  201. data/lib/arel/collectors/bind.rb +2 -2
  202. data/lib/arel/collectors/composite.rb +3 -3
  203. data/lib/arel/collectors/sql_string.rb +1 -1
  204. data/lib/arel/collectors/substitute_binds.rb +1 -1
  205. data/lib/arel/crud.rb +18 -22
  206. data/lib/arel/delete_manager.rb +2 -4
  207. data/lib/arel/insert_manager.rb +2 -3
  208. data/lib/arel/nodes/casted.rb +1 -1
  209. data/lib/arel/nodes/delete_statement.rb +8 -13
  210. data/lib/arel/nodes/homogeneous_in.rb +4 -0
  211. data/lib/arel/nodes/insert_statement.rb +2 -2
  212. data/lib/arel/nodes/select_core.rb +2 -2
  213. data/lib/arel/nodes/select_statement.rb +2 -2
  214. data/lib/arel/nodes/update_statement.rb +3 -2
  215. data/lib/arel/predications.rb +3 -3
  216. data/lib/arel/select_manager.rb +10 -4
  217. data/lib/arel/table.rb +0 -1
  218. data/lib/arel/tree_manager.rb +0 -12
  219. data/lib/arel/update_manager.rb +2 -4
  220. data/lib/arel/visitors/dot.rb +80 -90
  221. data/lib/arel/visitors/mysql.rb +6 -1
  222. data/lib/arel/visitors/postgresql.rb +0 -10
  223. data/lib/arel/visitors/to_sql.rb +44 -3
  224. data/lib/arel.rb +1 -1
  225. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  226. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  227. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  228. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  229. metadata +55 -16
@@ -23,8 +23,12 @@ module ActiveRecord
23
23
  @name_to_pool_config[shard]
24
24
  end
25
25
 
26
- def set_pool_config(_, shard, pool_config)
27
- @name_to_pool_config[shard] = pool_config
26
+ def set_pool_config(role, shard, pool_config)
27
+ if pool_config
28
+ @name_to_pool_config[shard] = pool_config
29
+ else
30
+ raise ArgumentError, "The `pool_config` for the :#{role} role and :#{shard} shard was `nil`. Please check your configuration. If you want your writing role to be something other than `:writing` set `config.active_record.writing_role` in your application configuration. The same setting should be applied for the `reading_role` if applicable."
31
+ end
28
32
  end
29
33
  end
30
34
  end
@@ -38,21 +38,16 @@ module ActiveRecord
38
38
  end
39
39
 
40
40
  # Executes the SQL statement in the context of this connection.
41
- def execute(sql, name = nil)
42
- if preventing_writes? && write_query?(sql)
43
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
44
- end
45
-
46
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
47
- # made since we established the connection
48
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
41
+ def execute(sql, name = nil, async: false)
42
+ sql = transform_query(sql)
43
+ check_if_write_query(sql)
49
44
 
50
- super
45
+ raw_execute(sql, name, async: async)
51
46
  end
52
47
 
53
- def exec_query(sql, name = "SQL", binds = [], prepare: false)
48
+ def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
54
49
  if without_prepared_statement?(binds)
55
- execute_and_free(sql, name) do |result|
50
+ execute_and_free(sql, name, async: async) do |result|
56
51
  if result
57
52
  build_result(columns: result.fields, rows: result.to_a)
58
53
  else
@@ -60,7 +55,7 @@ module ActiveRecord
60
55
  end
61
56
  end
62
57
  else
63
- exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
58
+ exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
64
59
  if result
65
60
  build_result(columns: result.fields, rows: result.to_a)
66
61
  else
@@ -70,7 +65,7 @@ module ActiveRecord
70
65
  end
71
66
  end
72
67
 
73
- def exec_delete(sql, name = nil, binds = [])
68
+ def exec_delete(sql, name = nil, binds = []) # :nodoc:
74
69
  if without_prepared_statement?(binds)
75
70
  @lock.synchronize do
76
71
  execute_and_free(sql, name) { @connection.affected_rows }
@@ -81,10 +76,28 @@ module ActiveRecord
81
76
  end
82
77
  alias :exec_update :exec_delete
83
78
 
79
+ # https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
80
+ # https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
81
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
82
+ private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
83
+
84
+ def high_precision_current_timestamp
85
+ HIGH_PRECISION_CURRENT_TIMESTAMP
86
+ end
87
+
84
88
  private
89
+ def raw_execute(sql, name, async: false)
90
+ # make sure we carry over any changes to ActiveRecord.default_timezone that have been
91
+ # made since we established the connection
92
+ @connection.query_options[:database_timezone] = ActiveRecord.default_timezone
93
+
94
+ super
95
+ end
96
+
85
97
  def execute_batch(statements, name = nil)
98
+ statements = statements.map { |sql| transform_query(sql) }
86
99
  combine_multi_statements(statements).each do |statement|
87
- execute(statement, name)
100
+ raw_execute(statement, name)
88
101
  end
89
102
  @connection.abandon_results!
90
103
  end
@@ -148,21 +161,20 @@ module ActiveRecord
148
161
  @max_allowed_packet ||= show_variable("max_allowed_packet")
149
162
  end
150
163
 
151
- def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
152
- if preventing_writes? && write_query?(sql)
153
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
154
- end
164
+ def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
165
+ sql = transform_query(sql)
166
+ check_if_write_query(sql)
155
167
 
156
168
  materialize_transactions
157
169
  mark_transaction_written_if_write(sql)
158
170
 
159
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
171
+ # make sure we carry over any changes to ActiveRecord.default_timezone that have been
160
172
  # made since we established the connection
161
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
173
+ @connection.query_options[:database_timezone] = ActiveRecord.default_timezone
162
174
 
163
175
  type_casted_binds = type_casted_binds(binds)
164
176
 
165
- log(sql, name, binds, type_casted_binds) do
177
+ log(sql, name, binds, type_casted_binds, async: async) do
166
178
  if cache_stmt
167
179
  stmt = @statements[sql] ||= @connection.prepare(sql)
168
180
  else
@@ -6,6 +6,21 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module MySQL
8
8
  module Quoting # :nodoc:
9
+ def quote_bound_value(value)
10
+ case value
11
+ when Numeric
12
+ _quote(value.to_s)
13
+ when BigDecimal
14
+ _quote(value.to_s("F"))
15
+ when true
16
+ "'1'"
17
+ when false
18
+ "'0'"
19
+ else
20
+ _quote(value)
21
+ end
22
+ end
23
+
9
24
  def quote_column_name(name)
10
25
  self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
11
26
  end
@@ -79,7 +94,7 @@ module ActiveRecord
79
94
  # We need to check explicitly for ActiveSupport::TimeWithZone because
80
95
  # we need to transform it to Time objects but we don't want to
81
96
  # transform Time objects to themselves.
82
- if ActiveRecord::Base.default_timezone == :utc
97
+ if ActiveRecord.default_timezone == :utc
83
98
  value.getutc
84
99
  else
85
100
  value.getlocal
@@ -167,6 +167,9 @@ module ActiveRecord
167
167
  elsif type_metadata.extra == "DEFAULT_GENERATED"
168
168
  default = +"(#{default})" unless default.start_with?("(")
169
169
  default, default_function = nil, default
170
+ elsif type_metadata.type == :text && default
171
+ # strip and unescape quotes
172
+ default = default[1...-1].gsub("\\'", "'")
170
173
  end
171
174
 
172
175
  MySQL::Column.new(
@@ -30,7 +30,11 @@ module ActiveRecord
30
30
 
31
31
  module ConnectionAdapters
32
32
  class Mysql2Adapter < AbstractMysqlAdapter
33
- ER_BAD_DB_ERROR = 1049
33
+ ER_BAD_DB_ERROR = 1049
34
+ ER_ACCESS_DENIED_ERROR = 1045
35
+ ER_CONN_HOST_ERROR = 2003
36
+ ER_UNKNOWN_HOST_ERROR = 2005
37
+
34
38
  ADAPTER_NAME = "Mysql2"
35
39
 
36
40
  include MySQL::DatabaseStatements
@@ -40,7 +44,11 @@ module ActiveRecord
40
44
  Mysql2::Client.new(config)
41
45
  rescue Mysql2::Error => error
42
46
  if error.error_number == ConnectionAdapters::Mysql2Adapter::ER_BAD_DB_ERROR
43
- raise ActiveRecord::NoDatabaseError
47
+ raise ActiveRecord::NoDatabaseError.db_error(config[:database])
48
+ elsif error.error_number == ConnectionAdapters::Mysql2Adapter::ER_ACCESS_DENIED_ERROR
49
+ raise ActiveRecord::DatabaseConnectionError.username_error(config[:username])
50
+ elsif [ConnectionAdapters::Mysql2Adapter::ER_CONN_HOST_ERROR, ConnectionAdapters::Mysql2Adapter::ER_UNKNOWN_HOST_ERROR].include?(error.error_number)
51
+ raise ActiveRecord::DatabaseConnectionError.hostname_error(config[:host])
44
52
  else
45
53
  raise ActiveRecord::ConnectionNotEstablished, error.message
46
54
  end
@@ -81,11 +89,9 @@ module ActiveRecord
81
89
 
82
90
  # HELPER METHODS ===========================================
83
91
 
84
- def each_hash(result) # :nodoc:
92
+ def each_hash(result, &block) # :nodoc:
85
93
  if block_given?
86
- result.each(as: :hash, symbolize_keys: true) do |row|
87
- yield row
88
- end
94
+ result.each(as: :hash, symbolize_keys: true, &block)
89
95
  else
90
96
  to_enum(:each_hash, result)
91
97
  end
@@ -26,9 +26,7 @@ module ActiveRecord
26
26
  end
27
27
 
28
28
  def connection_specification_name
29
- if connection_klass.is_a?(String)
30
- connection_klass
31
- elsif connection_klass.primary_class?
29
+ if connection_klass.primary_class?
32
30
  "ActiveRecord::Base"
33
31
  else
34
32
  connection_klass.name
@@ -36,7 +36,11 @@ module ActiveRecord
36
36
  end
37
37
 
38
38
  def set_pool_config(role, shard, pool_config)
39
- @name_to_role_mapping[role][shard] = pool_config
39
+ if pool_config
40
+ @name_to_role_mapping[role][shard] = pool_config
41
+ else
42
+ raise ArgumentError, "The `pool_config` for the :#{role} role and :#{shard} shard was `nil`. Please check your configuration. If you want your writing role to be something other than `:writing` set `config.active_record.writing_role` in your application configuration. The same setting should be applied for the `reading_role` if applicable."
43
+ end
40
44
  end
41
45
  end
42
46
  end
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  end
11
11
 
12
12
  # Queries the database and returns the results in an Array-like object
13
- def query(sql, name = nil) #:nodoc:
13
+ def query(sql, name = nil) # :nodoc:
14
14
  materialize_transactions
15
15
  mark_transaction_written_if_write(sql)
16
16
 
@@ -35,9 +35,8 @@ module ActiveRecord
35
35
  # Note: the PG::Result object is manually memory managed; if you don't
36
36
  # need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
37
37
  def execute(sql, name = nil)
38
- if preventing_writes? && write_query?(sql)
39
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
40
- end
38
+ sql = transform_query(sql)
39
+ check_if_write_query(sql)
41
40
 
42
41
  materialize_transactions
43
42
  mark_transaction_written_if_write(sql)
@@ -49,8 +48,8 @@ module ActiveRecord
49
48
  end
50
49
  end
51
50
 
52
- def exec_query(sql, name = "SQL", binds = [], prepare: false)
53
- execute_and_clear(sql, name, binds, prepare: prepare) do |result|
51
+ def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
52
+ execute_and_clear(sql, name, binds, prepare: prepare, async: async) do |result|
54
53
  types = {}
55
54
  fields = result.fields
56
55
  fields.each_with_index do |fname, i|
@@ -66,7 +65,7 @@ module ActiveRecord
66
65
  end
67
66
  end
68
67
 
69
- def exec_delete(sql, name = nil, binds = [])
68
+ def exec_delete(sql, name = nil, binds = []) # :nodoc:
70
69
  execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
71
70
  end
72
71
  alias :exec_update :exec_delete
@@ -86,7 +85,7 @@ module ActiveRecord
86
85
  end
87
86
  private :sql_for_insert
88
87
 
89
- def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
88
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) # :nodoc:
90
89
  if use_insert_returning? || pk == false
91
90
  super
92
91
  else
@@ -105,25 +104,33 @@ module ActiveRecord
105
104
  end
106
105
 
107
106
  # Begins a transaction.
108
- def begin_db_transaction
107
+ def begin_db_transaction # :nodoc:
109
108
  execute("BEGIN", "TRANSACTION")
110
109
  end
111
110
 
112
- def begin_isolated_db_transaction(isolation)
111
+ def begin_isolated_db_transaction(isolation) # :nodoc:
113
112
  begin_db_transaction
114
113
  execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
115
114
  end
116
115
 
117
116
  # Commits a transaction.
118
- def commit_db_transaction
117
+ def commit_db_transaction # :nodoc:
119
118
  execute("COMMIT", "TRANSACTION")
120
119
  end
121
120
 
122
121
  # Aborts a transaction.
123
- def exec_rollback_db_transaction
122
+ def exec_rollback_db_transaction # :nodoc:
124
123
  execute("ROLLBACK", "TRANSACTION")
125
124
  end
126
125
 
126
+ # From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
127
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
128
+ private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
129
+
130
+ def high_precision_current_timestamp
131
+ HIGH_PRECISION_CURRENT_TIMESTAMP
132
+ end
133
+
127
134
  private
128
135
  def execute_batch(statements, name = nil)
129
136
  execute(combine_multi_statements(statements))
@@ -16,6 +16,14 @@ module ActiveRecord
16
16
  super
17
17
  end
18
18
  end
19
+
20
+ def type_cast_for_schema(value)
21
+ case value
22
+ when ::Float::INFINITY then "::Float::INFINITY"
23
+ when -::Float::INFINITY then "-::Float::INFINITY"
24
+ else super
25
+ end
26
+ end
19
27
  end
20
28
  end
21
29
  end
@@ -24,6 +24,11 @@ module ActiveRecord
24
24
  else super
25
25
  end
26
26
  end
27
+
28
+ protected
29
+ def real_type_unless_aliased(real_type)
30
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type == real_type ? :datetime : real_type
31
+ end
27
32
  end
28
33
  end
29
34
  end
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "strscan"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
5
7
  module PostgreSQL
6
8
  module OID # :nodoc:
7
9
  class Hstore < Type::Value # :nodoc:
10
+ ERROR = "Invalid Hstore document: %s"
11
+
8
12
  include ActiveModel::Type::Helpers::Mutable
9
13
 
10
14
  def type
@@ -12,15 +16,56 @@ module ActiveRecord
12
16
  end
13
17
 
14
18
  def deserialize(value)
15
- if value.is_a?(::String)
16
- ::Hash[value.scan(HstorePair).map { |k, v|
17
- v = v.upcase == "NULL" ? nil : v.gsub(/\A"(.*)"\Z/m, '\1').gsub(/\\(.)/, '\1')
18
- k = k.gsub(/\A"(.*)"\Z/m, '\1').gsub(/\\(.)/, '\1')
19
- [k, v]
20
- }]
21
- else
22
- value
19
+ return value unless value.is_a?(::String)
20
+
21
+ scanner = StringScanner.new(value)
22
+ hash = {}
23
+
24
+ until scanner.eos?
25
+ unless scanner.skip(/"/)
26
+ raise(ArgumentError, ERROR % scanner.string.inspect)
27
+ end
28
+
29
+ unless key = scanner.scan_until(/(?<!\\)(?=")/)
30
+ raise(ArgumentError, ERROR % scanner.string.inspect)
31
+ end
32
+
33
+ unless scanner.skip(/"=>?/)
34
+ raise(ArgumentError, ERROR % scanner.string.inspect)
35
+ end
36
+
37
+ if scanner.scan(/NULL/)
38
+ value = nil
39
+ else
40
+ unless scanner.skip(/"/)
41
+ raise(ArgumentError, ERROR % scanner.string.inspect)
42
+ end
43
+
44
+ unless value = scanner.scan_until(/(?<!\\)(?=")/)
45
+ raise(ArgumentError, ERROR % scanner.string.inspect)
46
+ end
47
+
48
+ unless scanner.skip(/"/)
49
+ raise(ArgumentError, ERROR % scanner.string.inspect)
50
+ end
51
+ end
52
+
53
+ key.gsub!('\"', '"')
54
+ key.gsub!("\\\\", "\\")
55
+
56
+ if value
57
+ value.gsub!('\"', '"')
58
+ value.gsub!("\\\\", "\\")
59
+ end
60
+
61
+ hash[key] = value
62
+
63
+ unless scanner.skip(/, /) || scanner.eos?
64
+ raise(ArgumentError, ERROR % scanner.string.inspect)
65
+ end
23
66
  end
67
+
68
+ hash
24
69
  end
25
70
 
26
71
  def serialize(value)
@@ -46,12 +91,6 @@ module ActiveRecord
46
91
  end
47
92
 
48
93
  private
49
- HstorePair = begin
50
- quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
51
- unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
52
- /(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
53
- end
54
-
55
94
  def escape_hstore(value)
56
95
  if value.nil?
57
96
  "NULL"
@@ -88,7 +88,7 @@ module ActiveRecord
88
88
  if value.start_with?('"') && value.end_with?('"')
89
89
  unquoted_value = value[1..-2]
90
90
  unquoted_value.gsub!('""', '"')
91
- unquoted_value.gsub!('\\\\', '\\')
91
+ unquoted_value.gsub!("\\\\", "\\")
92
92
  unquoted_value
93
93
  else
94
94
  value
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ module OID # :nodoc:
7
+ class Timestamp < DateTime # :nodoc:
8
+ def type
9
+ real_type_unless_aliased(:timestamp)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end