activerecord 6.1.4.4 → 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 +729 -1176
  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 +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/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 +14 -7
  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 -9
  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 -21
  61. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
  62. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
  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 -12
  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 +26 -3
  80. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
  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 +113 -112
  86. data/lib/active_record/database_configurations/database_config.rb +12 -0
  87. data/lib/active_record/database_configurations/hash_config.rb +27 -1
  88. data/lib/active_record/database_configurations/url_config.rb +2 -2
  89. data/lib/active_record/database_configurations.rb +18 -9
  90. data/lib/active_record/delegated_type.rb +33 -11
  91. data/lib/active_record/destroy_association_async_job.rb +1 -1
  92. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  93. data/lib/active_record/dynamic_matchers.rb +1 -1
  94. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  95. data/lib/active_record/encryption/cipher.rb +53 -0
  96. data/lib/active_record/encryption/config.rb +44 -0
  97. data/lib/active_record/encryption/configurable.rb +61 -0
  98. data/lib/active_record/encryption/context.rb +35 -0
  99. data/lib/active_record/encryption/contexts.rb +72 -0
  100. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  101. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  102. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  103. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  104. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  105. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  106. data/lib/active_record/encryption/encryptor.rb +155 -0
  107. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  108. data/lib/active_record/encryption/errors.rb +15 -0
  109. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  110. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
  111. data/lib/active_record/encryption/key.rb +28 -0
  112. data/lib/active_record/encryption/key_generator.rb +42 -0
  113. data/lib/active_record/encryption/key_provider.rb +46 -0
  114. data/lib/active_record/encryption/message.rb +33 -0
  115. data/lib/active_record/encryption/message_serializer.rb +80 -0
  116. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  117. data/lib/active_record/encryption/properties.rb +76 -0
  118. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  119. data/lib/active_record/encryption/scheme.rb +99 -0
  120. data/lib/active_record/encryption.rb +55 -0
  121. data/lib/active_record/enum.rb +41 -41
  122. data/lib/active_record/errors.rb +66 -3
  123. data/lib/active_record/fixture_set/file.rb +15 -1
  124. data/lib/active_record/fixture_set/table_row.rb +40 -5
  125. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  126. data/lib/active_record/fixtures.rb +16 -11
  127. data/lib/active_record/future_result.rb +139 -0
  128. data/lib/active_record/gem_version.rb +4 -4
  129. data/lib/active_record/inheritance.rb +55 -17
  130. data/lib/active_record/insert_all.rb +34 -5
  131. data/lib/active_record/integration.rb +1 -1
  132. data/lib/active_record/internal_metadata.rb +3 -5
  133. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  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 +83 -1
  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 +46 -32
  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 +80 -56
  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 +41 -28
  158. data/lib/active_record/relation/delegation.rb +6 -6
  159. data/lib/active_record/relation/finder_methods.rb +32 -23
  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 +232 -49
  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 +10 -6
  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 +107 -23
  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 +52 -13
@@ -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
@@ -212,7 +233,9 @@ module ActiveRecord
212
233
  end
213
234
 
214
235
  def prepare_data_sources
215
- connection.data_sources.each { |source| @data_sources[source] = true }
236
+ tables_to_cache.each do |source|
237
+ @data_sources[source] = true
238
+ end
216
239
  end
217
240
 
218
241
  def open(filename)
@@ -18,10 +18,9 @@ module ActiveRecord
18
18
  SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
19
19
  end
20
20
 
21
- def execute(sql, name = nil) #:nodoc:
22
- if preventing_writes? && write_query?(sql)
23
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
24
- end
21
+ def execute(sql, name = nil) # :nodoc:
22
+ sql = transform_query(sql)
23
+ check_if_write_query(sql)
25
24
 
26
25
  materialize_transactions
27
26
  mark_transaction_written_if_write(sql)
@@ -33,17 +32,16 @@ module ActiveRecord
33
32
  end
34
33
  end
35
34
 
36
- def exec_query(sql, name = nil, binds = [], prepare: false)
37
- if preventing_writes? && write_query?(sql)
38
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
39
- end
35
+ def exec_query(sql, name = nil, binds = [], prepare: false, async: false) # :nodoc:
36
+ sql = transform_query(sql)
37
+ check_if_write_query(sql)
40
38
 
41
39
  materialize_transactions
42
40
  mark_transaction_written_if_write(sql)
43
41
 
44
42
  type_casted_binds = type_casted_binds(binds)
45
43
 
46
- log(sql, name, binds, type_casted_binds) do
44
+ log(sql, name, binds, type_casted_binds, async: async) do
47
45
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
48
46
  # Don't cache statements if they are not prepared
49
47
  unless prepare
@@ -70,13 +68,13 @@ module ActiveRecord
70
68
  end
71
69
  end
72
70
 
73
- def exec_delete(sql, name = "SQL", binds = [])
71
+ def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
74
72
  exec_query(sql, name, binds)
75
73
  @connection.changes
76
74
  end
77
75
  alias :exec_update :exec_delete
78
76
 
79
- def begin_isolated_db_transaction(isolation) #:nodoc
77
+ def begin_isolated_db_transaction(isolation) # :nodoc:
80
78
  raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
81
79
  raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
82
80
 
@@ -85,20 +83,29 @@ module ActiveRecord
85
83
  begin_db_transaction
86
84
  end
87
85
 
88
- def begin_db_transaction #:nodoc:
86
+ def begin_db_transaction # :nodoc:
89
87
  log("begin transaction", "TRANSACTION") { @connection.transaction }
90
88
  end
91
89
 
92
- def commit_db_transaction #:nodoc:
90
+ def commit_db_transaction # :nodoc:
93
91
  log("commit transaction", "TRANSACTION") { @connection.commit }
94
92
  reset_read_uncommitted
95
93
  end
96
94
 
97
- def exec_rollback_db_transaction #:nodoc:
95
+ def exec_rollback_db_transaction # :nodoc:
98
96
  log("rollback transaction", "TRANSACTION") { @connection.rollback }
99
97
  reset_read_uncommitted
100
98
  end
101
99
 
100
+ # https://stackoverflow.com/questions/17574784
101
+ # https://www.sqlite.org/lang_datefunc.html
102
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')").freeze # :nodoc:
103
+ private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
104
+
105
+ def high_precision_current_timestamp
106
+ HIGH_PRECISION_CURRENT_TIMESTAMP
107
+ end
108
+
102
109
  private
103
110
  def reset_read_uncommitted
104
111
  read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
@@ -108,11 +115,10 @@ module ActiveRecord
108
115
  end
109
116
 
110
117
  def execute_batch(statements, name = nil)
118
+ statements = statements.map { |sql| transform_query(sql) }
111
119
  sql = combine_multi_statements(statements)
112
120
 
113
- if preventing_writes? && write_query?(sql)
114
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
115
- end
121
+ check_if_write_query(sql)
116
122
 
117
123
  materialize_transactions
118
124
  mark_transaction_written_if_write(sql)
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  module SchemaStatements # :nodoc:
7
7
  # Returns an array of indexes for the given table.
8
8
  def indexes(table_name)
9
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
9
+ exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").filter_map do |row|
10
10
  # Indexes SQLite creates implicitly for internal use start with "sqlite_".
11
11
  # See https://www.sqlite.org/fileformat2.html#intschema
12
12
  next if row["name"].start_with?("sqlite_")
@@ -49,7 +49,7 @@ module ActiveRecord
49
49
  where: where,
50
50
  orders: orders
51
51
  )
52
- end.compact
52
+ end
53
53
  end
54
54
 
55
55
  def add_foreign_key(from_table, to_table, **options)
@@ -60,6 +60,8 @@ module ActiveRecord
60
60
  end
61
61
 
62
62
  def remove_foreign_key(from_table, to_table = nil, **options)
63
+ return if options[:if_exists] == true && !foreign_key_exists?(from_table, to_table)
64
+
63
65
  to_table ||= options[:to_table]
64
66
  options = options.except(:name, :to_table, :validate)
65
67
  foreign_keys = foreign_keys(from_table)