activerecord 6.1.6 → 7.0.0.alpha1

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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +728 -1279
  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 +31 -9
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  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 +14 -23
  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 -47
  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/connection_adapters/abstract/connection_handler.rb +312 -0
  48. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +11 -4
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +112 -66
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
  60. data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -23
  61. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
  62. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
  63. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  64. data/lib/active_record/connection_adapters/pool_config.rb +1 -3
  65. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -14
  66. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  69. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  70. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  73. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  74. data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
  75. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  76. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
  79. data/lib/active_record/connection_adapters/schema_cache.rb +36 -37
  80. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -19
  81. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  82. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  83. data/lib/active_record/connection_adapters.rb +6 -5
  84. data/lib/active_record/connection_handling.rb +20 -38
  85. data/lib/active_record/core.rb +111 -110
  86. data/lib/active_record/database_configurations/connection_url_resolver.rb +0 -1
  87. data/lib/active_record/database_configurations/database_config.rb +12 -0
  88. data/lib/active_record/database_configurations/hash_config.rb +27 -1
  89. data/lib/active_record/database_configurations/url_config.rb +2 -2
  90. data/lib/active_record/database_configurations.rb +17 -9
  91. data/lib/active_record/delegated_type.rb +33 -11
  92. data/lib/active_record/destroy_association_async_job.rb +1 -1
  93. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  94. data/lib/active_record/dynamic_matchers.rb +1 -1
  95. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  96. data/lib/active_record/encryption/cipher.rb +53 -0
  97. data/lib/active_record/encryption/config.rb +44 -0
  98. data/lib/active_record/encryption/configurable.rb +61 -0
  99. data/lib/active_record/encryption/context.rb +35 -0
  100. data/lib/active_record/encryption/contexts.rb +72 -0
  101. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  102. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  103. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  104. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  105. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  106. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  107. data/lib/active_record/encryption/encryptor.rb +155 -0
  108. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  109. data/lib/active_record/encryption/errors.rb +15 -0
  110. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  111. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
  112. data/lib/active_record/encryption/key.rb +28 -0
  113. data/lib/active_record/encryption/key_generator.rb +42 -0
  114. data/lib/active_record/encryption/key_provider.rb +46 -0
  115. data/lib/active_record/encryption/message.rb +33 -0
  116. data/lib/active_record/encryption/message_serializer.rb +80 -0
  117. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  118. data/lib/active_record/encryption/properties.rb +76 -0
  119. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  120. data/lib/active_record/encryption/scheme.rb +99 -0
  121. data/lib/active_record/encryption.rb +55 -0
  122. data/lib/active_record/enum.rb +41 -41
  123. data/lib/active_record/errors.rb +66 -3
  124. data/lib/active_record/fixture_set/file.rb +15 -1
  125. data/lib/active_record/fixture_set/table_row.rb +40 -5
  126. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  127. data/lib/active_record/fixtures.rb +16 -11
  128. data/lib/active_record/future_result.rb +139 -0
  129. data/lib/active_record/gem_version.rb +4 -4
  130. data/lib/active_record/inheritance.rb +55 -17
  131. data/lib/active_record/insert_all.rb +34 -5
  132. data/lib/active_record/integration.rb +1 -1
  133. data/lib/active_record/internal_metadata.rb +1 -5
  134. data/lib/active_record/locking/optimistic.rb +10 -9
  135. data/lib/active_record/log_subscriber.rb +6 -2
  136. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  137. data/lib/active_record/middleware/database_selector.rb +8 -3
  138. data/lib/active_record/migration/command_recorder.rb +4 -4
  139. data/lib/active_record/migration/compatibility.rb +89 -10
  140. data/lib/active_record/migration/join_table.rb +1 -1
  141. data/lib/active_record/migration.rb +109 -79
  142. data/lib/active_record/model_schema.rb +45 -31
  143. data/lib/active_record/nested_attributes.rb +3 -3
  144. data/lib/active_record/no_touching.rb +2 -2
  145. data/lib/active_record/null_relation.rb +2 -6
  146. data/lib/active_record/persistence.rb +134 -45
  147. data/lib/active_record/query_cache.rb +2 -2
  148. data/lib/active_record/query_logs.rb +203 -0
  149. data/lib/active_record/querying.rb +15 -5
  150. data/lib/active_record/railtie.rb +117 -17
  151. data/lib/active_record/railties/controller_runtime.rb +1 -1
  152. data/lib/active_record/railties/databases.rake +72 -48
  153. data/lib/active_record/readonly_attributes.rb +11 -0
  154. data/lib/active_record/reflection.rb +45 -44
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  156. data/lib/active_record/relation/batches.rb +3 -3
  157. data/lib/active_record/relation/calculations.rb +39 -26
  158. data/lib/active_record/relation/delegation.rb +6 -6
  159. data/lib/active_record/relation/finder_methods.rb +31 -22
  160. data/lib/active_record/relation/merger.rb +20 -13
  161. data/lib/active_record/relation/predicate_builder.rb +1 -6
  162. data/lib/active_record/relation/query_attribute.rb +5 -11
  163. data/lib/active_record/relation/query_methods.rb +230 -47
  164. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  165. data/lib/active_record/relation/spawn_methods.rb +2 -2
  166. data/lib/active_record/relation/where_clause.rb +8 -4
  167. data/lib/active_record/relation.rb +166 -77
  168. data/lib/active_record/result.rb +17 -2
  169. data/lib/active_record/runtime_registry.rb +2 -4
  170. data/lib/active_record/sanitization.rb +11 -7
  171. data/lib/active_record/schema_dumper.rb +3 -3
  172. data/lib/active_record/schema_migration.rb +0 -4
  173. data/lib/active_record/scoping/default.rb +61 -12
  174. data/lib/active_record/scoping/named.rb +3 -11
  175. data/lib/active_record/scoping.rb +40 -22
  176. data/lib/active_record/serialization.rb +1 -1
  177. data/lib/active_record/signed_id.rb +1 -1
  178. data/lib/active_record/tasks/database_tasks.rb +106 -22
  179. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  180. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
  181. data/lib/active_record/test_databases.rb +1 -1
  182. data/lib/active_record/test_fixtures.rb +4 -4
  183. data/lib/active_record/timestamp.rb +3 -4
  184. data/lib/active_record/transactions.rb +9 -14
  185. data/lib/active_record/translation.rb +2 -2
  186. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  187. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  188. data/lib/active_record/type/internal/timezone.rb +2 -2
  189. data/lib/active_record/type/serialized.rb +1 -1
  190. data/lib/active_record/type/type_map.rb +17 -20
  191. data/lib/active_record/type.rb +1 -2
  192. data/lib/active_record/validations/associated.rb +1 -1
  193. data/lib/active_record.rb +170 -2
  194. data/lib/arel/attributes/attribute.rb +0 -8
  195. data/lib/arel/crud.rb +18 -22
  196. data/lib/arel/delete_manager.rb +2 -4
  197. data/lib/arel/insert_manager.rb +2 -3
  198. data/lib/arel/nodes/casted.rb +1 -1
  199. data/lib/arel/nodes/delete_statement.rb +8 -13
  200. data/lib/arel/nodes/insert_statement.rb +2 -2
  201. data/lib/arel/nodes/select_core.rb +2 -2
  202. data/lib/arel/nodes/select_statement.rb +2 -2
  203. data/lib/arel/nodes/update_statement.rb +3 -2
  204. data/lib/arel/predications.rb +1 -1
  205. data/lib/arel/select_manager.rb +10 -4
  206. data/lib/arel/table.rb +0 -1
  207. data/lib/arel/tree_manager.rb +0 -12
  208. data/lib/arel/update_manager.rb +2 -4
  209. data/lib/arel/visitors/dot.rb +80 -90
  210. data/lib/arel/visitors/mysql.rb +6 -1
  211. data/lib/arel/visitors/postgresql.rb +0 -10
  212. data/lib/arel/visitors/to_sql.rb +43 -2
  213. data/lib/arel.rb +1 -1
  214. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  215. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  216. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  217. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  218. metadata +55 -17
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  module SchemaStatements
7
7
  # Drops the database specified on the +name+ attribute
8
8
  # and creates it again using the provided +options+.
9
- def recreate_database(name, options = {}) #:nodoc:
9
+ def recreate_database(name, options = {}) # :nodoc:
10
10
  drop_database(name)
11
11
  create_database(name, options)
12
12
  end
@@ -50,7 +50,7 @@ module ActiveRecord
50
50
  #
51
51
  # Example:
52
52
  # drop_database 'matt_development'
53
- def drop_database(name) #:nodoc:
53
+ def drop_database(name) # :nodoc:
54
54
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
55
55
  end
56
56
 
@@ -244,7 +244,7 @@ module ActiveRecord
244
244
  end
245
245
 
246
246
  # Returns the sequence name for a table's primary key or some other specified key.
247
- def default_sequence_name(table_name, pk = "id") #:nodoc:
247
+ def default_sequence_name(table_name, pk = "id") # :nodoc:
248
248
  result = serial_sequence(table_name, pk)
249
249
  return nil unless result
250
250
  Utils.extract_schema_qualified_name(result).to_s
@@ -257,7 +257,7 @@ module ActiveRecord
257
257
  end
258
258
 
259
259
  # Sets the sequence of a table's primary key to the specified value.
260
- def set_pk_sequence!(table, value) #:nodoc:
260
+ def set_pk_sequence!(table, value) # :nodoc:
261
261
  pk, sequence = pk_and_sequence_for(table)
262
262
 
263
263
  if pk
@@ -272,7 +272,7 @@ module ActiveRecord
272
272
  end
273
273
 
274
274
  # Resets the sequence of a table's primary key to the maximum value.
275
- def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
275
+ def reset_pk_sequence!(table, pk = nil, sequence = nil) # :nodoc:
276
276
  unless pk && sequence
277
277
  default_pk, default_sequence = pk_and_sequence_for(table)
278
278
 
@@ -300,7 +300,7 @@ module ActiveRecord
300
300
  end
301
301
 
302
302
  # Returns a table's primary key and belonging sequence.
303
- def pk_and_sequence_for(table) #:nodoc:
303
+ def pk_and_sequence_for(table) # :nodoc:
304
304
  # First try looking for a sequence with a dependency on the
305
305
  # given table's primary key.
306
306
  result = query(<<~SQL, "SCHEMA")[0]
@@ -393,13 +393,13 @@ module ActiveRecord
393
393
  rename_table_indexes(table_name, new_name)
394
394
  end
395
395
 
396
- def add_column(table_name, column_name, type, **options) #:nodoc:
396
+ def add_column(table_name, column_name, type, **options) # :nodoc:
397
397
  clear_cache!
398
398
  super
399
399
  change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
400
400
  end
401
401
 
402
- def change_column(table_name, column_name, type, **options) #:nodoc:
402
+ def change_column(table_name, column_name, type, **options) # :nodoc:
403
403
  clear_cache!
404
404
  sqls, procs = Array(change_column_for_alter(table_name, column_name, type, **options)).partition { |v| v.is_a?(String) }
405
405
  execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
@@ -411,7 +411,7 @@ module ActiveRecord
411
411
  execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
412
412
  end
413
413
 
414
- def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
414
+ def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
415
415
  clear_cache!
416
416
  unless null || default.nil?
417
417
  column = column_for(table_name, column_name)
@@ -435,13 +435,13 @@ module ActiveRecord
435
435
  end
436
436
 
437
437
  # Renames a column in a table.
438
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
438
+ def rename_column(table_name, column_name, new_column_name) # :nodoc:
439
439
  clear_cache!
440
440
  execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
441
441
  rename_column_indexes(table_name, column_name, new_column_name)
442
442
  end
443
443
 
444
- def add_index(table_name, column_name, **options) #:nodoc:
444
+ def add_index(table_name, column_name, **options) # :nodoc:
445
445
  index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
446
446
 
447
447
  create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
@@ -576,7 +576,7 @@ module ActiveRecord
576
576
 
577
577
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
578
578
  # requires that the ORDER BY include the distinct column.
579
- def columns_for_distinct(columns, orders) #:nodoc:
579
+ def columns_for_distinct(columns, orders) # :nodoc:
580
580
  order_columns = orders.compact_blank.map { |s|
581
581
  # Convert Arel node to string
582
582
  s = visitor.compile(s) unless s.is_a?(String)
@@ -52,7 +52,7 @@ module ActiveRecord
52
52
  # * <tt>:port</tt> - Defaults to 5432.
53
53
  # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
54
54
  # * <tt>:password</tt> - Password to be used if the server demands password authentication.
55
- # * <tt>:database</tt> - Defaults to be the same as the user name.
55
+ # * <tt>:database</tt> - Defaults to be the same as the username.
56
56
  # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
57
57
  # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
58
58
  # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
@@ -78,7 +78,11 @@ module ActiveRecord
78
78
  PG.connect(conn_params)
79
79
  rescue ::PG::Error => error
80
80
  if conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
81
- raise ActiveRecord::NoDatabaseError
81
+ raise ActiveRecord::NoDatabaseError.db_error(conn_params[:dbname])
82
+ elsif conn_params && conn_params[:user] && error.message.include?(conn_params[:user])
83
+ raise ActiveRecord::DatabaseConnectionError.username_error(conn_params[:user])
84
+ elsif conn_params && conn_params[:hostname] && error.message.include?(conn_params[:hostname])
85
+ raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:hostname])
82
86
  else
83
87
  raise ActiveRecord::ConnectionNotEstablished, error.message
84
88
  end
@@ -98,6 +102,24 @@ module ActiveRecord
98
102
  # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
99
103
  class_attribute :create_unlogged_tables, default: false
100
104
 
105
+ ##
106
+ # :singleton-method:
107
+ # PostgreSQL supports multiple types for DateTimes. By default if you use `datetime`
108
+ # in migrations, Rails will translate this to a PostgreSQL "timestamp without time zone".
109
+ # Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
110
+ # store DateTimes as "timestamp with time zone":
111
+ #
112
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
113
+ #
114
+ # Or if you are adding a custom type:
115
+ #
116
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
117
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
118
+ #
119
+ # If you're using :ruby as your config.active_record.schema_format and you change this
120
+ # setting, you should immediately run bin/rails db:migrate to update the types in your schema.rb.
121
+ class_attribute :datetime_type, default: :timestamp
122
+
101
123
  NATIVE_DATABASE_TYPES = {
102
124
  primary_key: "bigserial primary key",
103
125
  string: { name: "character varying" },
@@ -105,7 +127,9 @@ module ActiveRecord
105
127
  integer: { name: "integer", limit: 4 },
106
128
  float: { name: "float" },
107
129
  decimal: { name: "decimal" },
108
- datetime: { name: "timestamp" },
130
+ datetime: {}, # set dynamically based on datetime_type
131
+ timestamp: { name: "timestamp" },
132
+ timestamptz: { name: "timestamptz" },
109
133
  time: { name: "time" },
110
134
  date: { name: "date" },
111
135
  daterange: { name: "daterange" },
@@ -141,7 +165,7 @@ module ActiveRecord
141
165
  oid: { name: "oid" },
142
166
  }
143
167
 
144
- OID = PostgreSQL::OID #:nodoc:
168
+ OID = PostgreSQL::OID # :nodoc:
145
169
 
146
170
  include PostgreSQL::Quoting
147
171
  include PostgreSQL::ReferentialIntegrity
@@ -272,19 +296,25 @@ module ActiveRecord
272
296
  # Is this connection alive and ready for queries?
273
297
  def active?
274
298
  @lock.synchronize do
275
- @connection.query "SELECT 1"
299
+ @connection.query ";"
276
300
  end
277
301
  true
278
302
  rescue PG::Error
279
303
  false
280
304
  end
281
305
 
306
+ def reload_type_map # :nodoc:
307
+ type_map.clear
308
+ initialize_type_map
309
+ end
310
+
282
311
  # Close then reopen the connection.
283
312
  def reconnect!
284
313
  @lock.synchronize do
285
314
  super
286
315
  @connection.reset
287
316
  configure_connection
317
+ reload_type_map
288
318
  rescue PG::ConnectionBad
289
319
  connect
290
320
  end
@@ -317,8 +347,16 @@ module ActiveRecord
317
347
  @connection = nil
318
348
  end
319
349
 
320
- def native_database_types #:nodoc:
321
- NATIVE_DATABASE_TYPES
350
+ def native_database_types # :nodoc:
351
+ self.class.native_database_types
352
+ end
353
+
354
+ def self.native_database_types # :nodoc:
355
+ @native_database_types ||= begin
356
+ types = NATIVE_DATABASE_TYPES.dup
357
+ types[:datetime] = types[datetime_type]
358
+ types
359
+ end
322
360
  end
323
361
 
324
362
  def set_standard_conforming_strings
@@ -438,8 +476,12 @@ module ActiveRecord
438
476
  sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
439
477
  elsif insert.update_duplicates?
440
478
  sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
441
- sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
442
- sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
479
+ if insert.raw_update_sql?
480
+ sql << insert.raw_update_sql
481
+ else
482
+ sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
483
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
484
+ end
443
485
  end
444
486
 
445
487
  sql << " RETURNING #{insert.returning}" if insert.returning
@@ -452,68 +494,8 @@ module ActiveRecord
452
494
  end
453
495
  end
454
496
 
455
- private
456
- # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
457
- VALUE_LIMIT_VIOLATION = "22001"
458
- NUMERIC_VALUE_OUT_OF_RANGE = "22003"
459
- NOT_NULL_VIOLATION = "23502"
460
- FOREIGN_KEY_VIOLATION = "23503"
461
- UNIQUE_VIOLATION = "23505"
462
- SERIALIZATION_FAILURE = "40001"
463
- DEADLOCK_DETECTED = "40P01"
464
- DUPLICATE_DATABASE = "42P04"
465
- LOCK_NOT_AVAILABLE = "55P03"
466
- QUERY_CANCELED = "57014"
467
-
468
- def translate_exception(exception, message:, sql:, binds:)
469
- return exception unless exception.respond_to?(:result)
470
-
471
- case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
472
- when nil
473
- if exception.message.match?(/connection is closed/i)
474
- ConnectionNotEstablished.new(exception)
475
- else
476
- super
477
- end
478
- when UNIQUE_VIOLATION
479
- RecordNotUnique.new(message, sql: sql, binds: binds)
480
- when FOREIGN_KEY_VIOLATION
481
- InvalidForeignKey.new(message, sql: sql, binds: binds)
482
- when VALUE_LIMIT_VIOLATION
483
- ValueTooLong.new(message, sql: sql, binds: binds)
484
- when NUMERIC_VALUE_OUT_OF_RANGE
485
- RangeError.new(message, sql: sql, binds: binds)
486
- when NOT_NULL_VIOLATION
487
- NotNullViolation.new(message, sql: sql, binds: binds)
488
- when SERIALIZATION_FAILURE
489
- SerializationFailure.new(message, sql: sql, binds: binds)
490
- when DEADLOCK_DETECTED
491
- Deadlocked.new(message, sql: sql, binds: binds)
492
- when DUPLICATE_DATABASE
493
- DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
494
- when LOCK_NOT_AVAILABLE
495
- LockWaitTimeout.new(message, sql: sql, binds: binds)
496
- when QUERY_CANCELED
497
- QueryCanceled.new(message, sql: sql, binds: binds)
498
- else
499
- super
500
- end
501
- end
502
-
503
- def get_oid_type(oid, fmod, column_name, sql_type = "")
504
- if !type_map.key?(oid)
505
- load_additional_types([oid])
506
- end
507
-
508
- type_map.fetch(oid, fmod, sql_type) {
509
- warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
510
- Type.default_value.tap do |cast_type|
511
- type_map.register_type(oid, cast_type)
512
- end
513
- }
514
- end
515
-
516
- def initialize_type_map(m = type_map)
497
+ class << self
498
+ def initialize_type_map(m) # :nodoc:
517
499
  m.register_type "int2", Type::Integer.new(limit: 2)
518
500
  m.register_type "int4", Type::Integer.new(limit: 4)
519
501
  m.register_type "int8", Type::Integer.new(limit: 8)
@@ -528,7 +510,6 @@ module ActiveRecord
528
510
  m.register_type "bool", Type::Boolean.new
529
511
  register_class_with_limit m, "bit", OID::Bit
530
512
  register_class_with_limit m, "varbit", OID::BitVarying
531
- m.alias_type "timestamptz", "timestamp"
532
513
  m.register_type "date", OID::Date.new
533
514
 
534
515
  m.register_type "money", OID::Money.new
@@ -553,7 +534,8 @@ module ActiveRecord
553
534
  m.register_type "circle", OID::SpecializedString.new(:circle)
554
535
 
555
536
  register_class_with_precision m, "time", Type::Time
556
- register_class_with_precision m, "timestamp", OID::DateTime
537
+ register_class_with_precision m, "timestamp", OID::Timestamp
538
+ register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
557
539
 
558
540
  m.register_type "numeric" do |_, fmod, sql_type|
559
541
  precision = extract_precision(sql_type)
@@ -579,7 +561,16 @@ module ActiveRecord
579
561
  precision = extract_precision(sql_type)
580
562
  OID::Interval.new(precision: precision)
581
563
  end
564
+ end
565
+ end
566
+
567
+ private
568
+ def type_map
569
+ @type_map ||= Type::HashLookupTypeMap.new
570
+ end
582
571
 
572
+ def initialize_type_map(m = type_map)
573
+ self.class.initialize_type_map(m)
583
574
  load_additional_types
584
575
  end
585
576
 
@@ -587,7 +578,7 @@ module ActiveRecord
587
578
  def extract_value_from_default(default)
588
579
  case default
589
580
  # Quoted types
590
- when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
581
+ when /\A[(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
591
582
  # The default 'now'::date is CURRENT_DATE
592
583
  if $1 == "now" && $2 == "date"
593
584
  nil
@@ -618,37 +609,100 @@ module ActiveRecord
618
609
  !default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
619
610
  end
620
611
 
612
+ # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
613
+ VALUE_LIMIT_VIOLATION = "22001"
614
+ NUMERIC_VALUE_OUT_OF_RANGE = "22003"
615
+ NOT_NULL_VIOLATION = "23502"
616
+ FOREIGN_KEY_VIOLATION = "23503"
617
+ UNIQUE_VIOLATION = "23505"
618
+ SERIALIZATION_FAILURE = "40001"
619
+ DEADLOCK_DETECTED = "40P01"
620
+ DUPLICATE_DATABASE = "42P04"
621
+ LOCK_NOT_AVAILABLE = "55P03"
622
+ QUERY_CANCELED = "57014"
623
+
624
+ def translate_exception(exception, message:, sql:, binds:)
625
+ return exception unless exception.respond_to?(:result)
626
+
627
+ case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
628
+ when nil
629
+ if exception.message.match?(/connection is closed/i)
630
+ ConnectionNotEstablished.new(exception)
631
+ else
632
+ super
633
+ end
634
+ when UNIQUE_VIOLATION
635
+ RecordNotUnique.new(message, sql: sql, binds: binds)
636
+ when FOREIGN_KEY_VIOLATION
637
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
638
+ when VALUE_LIMIT_VIOLATION
639
+ ValueTooLong.new(message, sql: sql, binds: binds)
640
+ when NUMERIC_VALUE_OUT_OF_RANGE
641
+ RangeError.new(message, sql: sql, binds: binds)
642
+ when NOT_NULL_VIOLATION
643
+ NotNullViolation.new(message, sql: sql, binds: binds)
644
+ when SERIALIZATION_FAILURE
645
+ SerializationFailure.new(message, sql: sql, binds: binds)
646
+ when DEADLOCK_DETECTED
647
+ Deadlocked.new(message, sql: sql, binds: binds)
648
+ when DUPLICATE_DATABASE
649
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
650
+ when LOCK_NOT_AVAILABLE
651
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
652
+ when QUERY_CANCELED
653
+ QueryCanceled.new(message, sql: sql, binds: binds)
654
+ else
655
+ super
656
+ end
657
+ end
658
+
659
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
660
+ if !type_map.key?(oid)
661
+ load_additional_types([oid])
662
+ end
663
+
664
+ type_map.fetch(oid, fmod, sql_type) {
665
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
666
+ Type.default_value.tap do |cast_type|
667
+ type_map.register_type(oid, cast_type)
668
+ end
669
+ }
670
+ end
671
+
621
672
  def load_additional_types(oids = nil)
622
673
  initializer = OID::TypeMapInitializer.new(type_map)
674
+ load_types_queries(initializer, oids) do |query|
675
+ execute_and_clear(query, "SCHEMA", []) do |records|
676
+ initializer.run(records)
677
+ end
678
+ end
679
+ end
623
680
 
681
+ def load_types_queries(initializer, oids)
624
682
  query = <<~SQL
625
683
  SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
626
684
  FROM pg_type as t
627
685
  LEFT JOIN pg_range as r ON oid = rngtypid
628
686
  SQL
629
-
630
687
  if oids
631
- query += "WHERE t.oid IN (%s)" % oids.join(", ")
688
+ yield query + "WHERE t.oid IN (%s)" % oids.join(", ")
632
689
  else
633
- query += initializer.query_conditions_for_initial_load
634
- end
635
-
636
- execute_and_clear(query, "SCHEMA", []) do |records|
637
- initializer.run(records)
690
+ yield query + initializer.query_conditions_for_known_type_names
691
+ yield query + initializer.query_conditions_for_known_type_types
692
+ yield query + initializer.query_conditions_for_array_types
638
693
  end
639
694
  end
640
695
 
641
- FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
696
+ FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
642
697
 
643
- def execute_and_clear(sql, name, binds, prepare: false)
644
- if preventing_writes? && write_query?(sql)
645
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
646
- end
698
+ def execute_and_clear(sql, name, binds, prepare: false, async: false)
699
+ sql = transform_query(sql)
700
+ check_if_write_query(sql)
647
701
 
648
702
  if !prepare || without_prepared_statement?(binds)
649
- result = exec_no_cache(sql, name, binds)
703
+ result = exec_no_cache(sql, name, binds, async: async)
650
704
  else
651
- result = exec_cache(sql, name, binds)
705
+ result = exec_cache(sql, name, binds, async: async)
652
706
  end
653
707
  begin
654
708
  ret = yield result
@@ -658,23 +712,23 @@ module ActiveRecord
658
712
  ret
659
713
  end
660
714
 
661
- def exec_no_cache(sql, name, binds)
715
+ def exec_no_cache(sql, name, binds, async: false)
662
716
  materialize_transactions
663
717
  mark_transaction_written_if_write(sql)
664
718
 
665
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
719
+ # make sure we carry over any changes to ActiveRecord.default_timezone that have been
666
720
  # made since we established the connection
667
721
  update_typemap_for_default_timezone
668
722
 
669
723
  type_casted_binds = type_casted_binds(binds)
670
- log(sql, name, binds, type_casted_binds) do
724
+ log(sql, name, binds, type_casted_binds, async: async) do
671
725
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
672
726
  @connection.exec_params(sql, type_casted_binds)
673
727
  end
674
728
  end
675
729
  end
676
730
 
677
- def exec_cache(sql, name, binds)
731
+ def exec_cache(sql, name, binds, async: false)
678
732
  materialize_transactions
679
733
  mark_transaction_written_if_write(sql)
680
734
  update_typemap_for_default_timezone
@@ -682,7 +736,7 @@ module ActiveRecord
682
736
  stmt_key = prepare_statement(sql, binds)
683
737
  type_casted_binds = type_casted_binds(binds)
684
738
 
685
- log(sql, name, binds, type_casted_binds, stmt_key) do
739
+ log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
686
740
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
687
741
  @connection.exec_prepared(stmt_key, type_casted_binds)
688
742
  end
@@ -776,7 +830,7 @@ module ActiveRecord
776
830
  # If using Active Record's time zone support configure the connection to return
777
831
  # TIMESTAMP WITH ZONE types in UTC.
778
832
  unless variables["timezone"]
779
- if ActiveRecord::Base.default_timezone == :utc
833
+ if ActiveRecord.default_timezone == :utc
780
834
  variables["timezone"] = "UTC"
781
835
  elsif @local_tz
782
836
  variables["timezone"] = @local_tz
@@ -875,14 +929,19 @@ module ActiveRecord
875
929
  end
876
930
 
877
931
  def update_typemap_for_default_timezone
878
- if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
879
- decoder_class = ActiveRecord::Base.default_timezone == :utc ?
932
+ if @default_timezone != ActiveRecord.default_timezone && @timestamp_decoder
933
+ decoder_class = ActiveRecord.default_timezone == :utc ?
880
934
  PG::TextDecoder::TimestampUtc :
881
935
  PG::TextDecoder::TimestampWithoutTimeZone
882
936
 
883
937
  @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
884
938
  @connection.type_map_for_results.add_coder(@timestamp_decoder)
885
- @default_timezone = ActiveRecord::Base.default_timezone
939
+
940
+ @default_timezone = ActiveRecord.default_timezone
941
+
942
+ # if default timezone has changed, we need to reconfigure the connection
943
+ # (specifically, the session time zone)
944
+ configure_connection
886
945
  end
887
946
  end
888
947
 
@@ -910,9 +969,7 @@ module ActiveRecord
910
969
  WHERE t.typname IN (%s)
911
970
  SQL
912
971
  coders = execute_and_clear(query, "SCHEMA", []) do |result|
913
- result
914
- .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
915
- .compact
972
+ result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
916
973
  end
917
974
 
918
975
  map = PG::TypeMapByOid.new
@@ -86,6 +86,7 @@ module ActiveRecord
86
86
 
87
87
  # A cached lookup for table existence.
88
88
  def data_source_exists?(name)
89
+ return if ignored_table?(name)
89
90
  prepare_data_sources if @data_sources.empty?
90
91
  return @data_sources[name] if @data_sources.key? name
91
92
 
@@ -108,6 +109,10 @@ module ActiveRecord
108
109
 
109
110
  # Get the columns for a table
110
111
  def columns(table_name)
112
+ if ignored_table?(table_name)
113
+ raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist"
114
+ end
115
+
111
116
  @columns.fetch(table_name) do
112
117
  @columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
113
118
  end
@@ -128,7 +133,11 @@ module ActiveRecord
128
133
 
129
134
  def indexes(table_name)
130
135
  @indexes.fetch(table_name) do
131
- @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
136
+ if data_source_exists?(table_name)
137
+ @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
138
+ else
139
+ []
140
+ end
132
141
  end
133
142
  end
134
143
 
@@ -162,7 +171,7 @@ module ActiveRecord
162
171
 
163
172
  def dump_to(filename)
164
173
  clear!
165
- connection.data_sources.each { |table| add(table) }
174
+ tables_to_cache.each { |table| add(table) }
166
175
  open(filename) { |f|
167
176
  if filename.include?(".dump")
168
177
  f.write(Marshal.dump(self))
@@ -186,6 +195,18 @@ module ActiveRecord
186
195
  end
187
196
 
188
197
  private
198
+ def tables_to_cache
199
+ connection.data_sources.reject do |table|
200
+ ignored_table?(table)
201
+ end
202
+ end
203
+
204
+ def ignored_table?(table_name)
205
+ ActiveRecord.schema_cache_ignored_tables.any? do |ignored|
206
+ ignored === table_name
207
+ end
208
+ end
209
+
189
210
  def reset_version!
190
211
  @version = connection.migration_context.current_version
191
212
  end
@@ -198,45 +219,23 @@ module ActiveRecord
198
219
  @indexes = deep_deduplicate(@indexes)
199
220
  end
200
221
 
201
- if RUBY_VERSION < "2.7"
202
- def deep_deduplicate(value)
203
- case value
204
- when Hash
205
- value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
206
- when Array
207
- value.map { |i| deep_deduplicate(i) }
208
- when String
209
- if value.tainted?
210
- # Ruby 2.6 and 2.7 have slightly different implementations of the String#-@ method.
211
- # In Ruby 2.6, the receiver of the String#-@ method is modified under certain
212
- # circumstances, and this was later identified as a bug
213
- # (https://bugs.ruby-lang.org/issues/15926) and only fixed in Ruby 2.7.
214
- value = value.dup
215
- end
216
- -value
217
- when Deduplicable
218
- -value
219
- else
220
- value
221
- end
222
- end
223
- else
224
- def deep_deduplicate(value)
225
- case value
226
- when Hash
227
- value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
228
- when Array
229
- value.map { |i| deep_deduplicate(i) }
230
- when String, Deduplicable
231
- -value
232
- else
233
- value
234
- end
222
+ def deep_deduplicate(value)
223
+ case value
224
+ when Hash
225
+ value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
226
+ when Array
227
+ value.map { |i| deep_deduplicate(i) }
228
+ when String, Deduplicable
229
+ -value
230
+ else
231
+ value
235
232
  end
236
233
  end
237
234
 
238
235
  def prepare_data_sources
239
- connection.data_sources.each { |source| @data_sources[source] = true }
236
+ tables_to_cache.each do |source|
237
+ @data_sources[source] = true
238
+ end
240
239
  end
241
240
 
242
241
  def open(filename)