aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md49
-rw-r--r--activerecord/lib/active_record/aggregations.rb1
-rw-r--r--activerecord/lib/active_record/association_relation.rb1
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb1
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb3
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb1
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb1
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb4
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb1
-rw-r--r--activerecord/lib/active_record/attribute_decorators.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb57
-rw-r--r--activerecord/lib/active_record/attribute_methods/before_type_cast.rb1
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb1
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb1
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb1
-rw-r--r--activerecord/lib/active_record/attributes.rb1
-rw-r--r--activerecord/lib/active_record/autosave_association.rb6
-rw-r--r--activerecord/lib/active_record/base.rb1
-rw-r--r--activerecord/lib/active_record/callbacks.rb1
-rw-r--r--activerecord/lib/active_record/coders/yaml_column.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb95
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb63
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb39
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb56
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb62
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/deduplicable.rb29
-rw-r--r--activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/quoting.rb51
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/column.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb41
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/utils.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/schema_cache.rb55
-rw-r--r--activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb41
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb17
-rw-r--r--activerecord/lib/active_record/connection_adapters/statement_pool.rb1
-rw-r--r--activerecord/lib/active_record/connection_handling.rb9
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/database_configurations.rb40
-rw-r--r--activerecord/lib/active_record/database_configurations/url_config.rb1
-rw-r--r--activerecord/lib/active_record/dynamic_matchers.rb1
-rw-r--r--activerecord/lib/active_record/errors.rb16
-rw-r--r--activerecord/lib/active_record/explain.rb1
-rw-r--r--activerecord/lib/active_record/fixture_set/table_row.rb1
-rw-r--r--activerecord/lib/active_record/fixture_set/table_rows.rb1
-rw-r--r--activerecord/lib/active_record/fixtures.rb3
-rw-r--r--activerecord/lib/active_record/inheritance.rb3
-rw-r--r--activerecord/lib/active_record/integration.rb2
-rw-r--r--activerecord/lib/active_record/internal_metadata.rb4
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb1
-rw-r--r--activerecord/lib/active_record/middleware/database_selector.rb1
-rw-r--r--activerecord/lib/active_record/middleware/database_selector/resolver.rb5
-rw-r--r--activerecord/lib/active_record/migration.rb52
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb1
-rw-r--r--activerecord/lib/active_record/migration/join_table.rb1
-rw-r--r--activerecord/lib/active_record/model_schema.rb5
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb3
-rw-r--r--activerecord/lib/active_record/null_relation.rb1
-rw-r--r--activerecord/lib/active_record/persistence.rb3
-rw-r--r--activerecord/lib/active_record/railtie.rb1
-rw-r--r--activerecord/lib/active_record/railties/databases.rake89
-rw-r--r--activerecord/lib/active_record/readonly_attributes.rb4
-rw-r--r--activerecord/lib/active_record/reflection.rb2
-rw-r--r--activerecord/lib/active_record/relation/batches.rb1
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb2
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb3
-rw-r--r--activerecord/lib/active_record/relation/merger.rb1
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb42
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb1
-rw-r--r--activerecord/lib/active_record/relation/where_clause.rb1
-rw-r--r--activerecord/lib/active_record/result.rb1
-rw-r--r--activerecord/lib/active_record/sanitization.rb32
-rw-r--r--activerecord/lib/active_record/schema.rb2
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb6
-rw-r--r--activerecord/lib/active_record/schema_migration.rb4
-rw-r--r--activerecord/lib/active_record/scoping.rb1
-rw-r--r--activerecord/lib/active_record/scoping/default.rb1
-rw-r--r--activerecord/lib/active_record/scoping/named.rb1
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb51
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb5
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb1
-rw-r--r--activerecord/lib/active_record/tasks/sqlite_database_tasks.rb1
-rw-r--r--activerecord/lib/active_record/test_fixtures.rb1
-rw-r--r--activerecord/lib/active_record/timestamp.rb43
-rw-r--r--activerecord/lib/active_record/touch_later.rb1
-rw-r--r--activerecord/lib/active_record/transactions.rb1
-rw-r--r--activerecord/lib/active_record/type.rb1
-rw-r--r--activerecord/lib/active_record/type/adapter_specific_registry.rb3
-rw-r--r--activerecord/lib/active_record/type/hash_lookup_type_map.rb1
-rw-r--r--activerecord/lib/active_record/type/serialized.rb1
-rw-r--r--activerecord/lib/active_record/type/type_map.rb1
-rw-r--r--activerecord/lib/active_record/type/unsigned_integer.rb1
-rw-r--r--activerecord/lib/active_record/validations.rb1
-rw-r--r--activerecord/lib/active_record/validations/associated.rb1
-rw-r--r--activerecord/lib/arel.rb2
-rw-r--r--activerecord/lib/arel/attributes.rb22
-rw-r--r--activerecord/lib/arel/nodes/node.rb8
-rw-r--r--activerecord/lib/arel/predications.rb5
-rw-r--r--activerecord/lib/arel/visitors.rb1
-rw-r--r--activerecord/lib/arel/visitors/depth_first.rb204
-rw-r--r--activerecord/lib/arel/visitors/dot.rb1
-rw-r--r--activerecord/lib/arel/visitors/mssql.rb1
-rw-r--r--activerecord/lib/arel/visitors/oracle.rb1
-rw-r--r--activerecord/lib/arel/visitors/oracle12.rb1
-rw-r--r--activerecord/lib/arel/visitors/postgresql.rb1
-rw-r--r--activerecord/lib/arel/visitors/sqlite.rb1
-rw-r--r--activerecord/lib/arel/visitors/to_sql.rb56
-rw-r--r--activerecord/lib/arel/visitors/visitor.rb15
-rw-r--r--activerecord/lib/arel/visitors/where_sql.rb1
-rw-r--r--activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb1
-rw-r--r--activerecord/lib/rails/generators/active_record/migration.rb1
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb1
-rw-r--r--activerecord/test/active_record/connection_adapters/fake_adapter.rb3
-rw-r--r--activerecord/test/cases/adapter_test.rb12
-rw-r--r--activerecord/test/cases/adapters/mysql2/annotate_test.rb37
-rw-r--r--activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb10
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb1
-rw-r--r--activerecord/test/cases/adapters/mysql2/enum_test.rb14
-rw-r--r--activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb45
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb1
-rw-r--r--activerecord/test/cases/adapters/mysql2/set_test.rb32
-rw-r--r--activerecord/test/cases/adapters/mysql2/table_options_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/transaction_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/annotate_test.rb37
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb1
-rw-r--r--activerecord/test/cases/adapters/postgresql/extension_migration_test.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/geometric_test.rb1
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb34
-rw-r--r--activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb1
-rw-r--r--activerecord/test/cases/adapters/postgresql/rename_table_test.rb1
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/transaction_test.rb1
-rw-r--r--activerecord/test/cases/adapters/postgresql/uuid_test.rb8
-rw-r--r--activerecord/test/cases/adapters/sqlite3/annotate_test.rb37
-rw-r--r--activerecord/test/cases/adapters/sqlite3/collation_test.rb9
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb31
-rw-r--r--activerecord/test/cases/annotate_test.rb46
-rw-r--r--activerecord/test/cases/ar_schema_test.rb27
-rw-r--r--activerecord/test/cases/arel/attributes/attribute_test.rb24
-rw-r--r--activerecord/test/cases/arel/attributes_test.rb41
-rw-r--r--activerecord/test/cases/arel/nodes/node_test.rb19
-rw-r--r--activerecord/test/cases/arel/select_manager_test.rb10
-rw-r--r--activerecord/test/cases/arel/visitors/depth_first_test.rb276
-rw-r--r--activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb8
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb24
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb1
-rw-r--r--activerecord/test/cases/associations/eager_test.rb25
-rw-r--r--activerecord/test/cases/associations/extension_test.rb1
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb1
-rw-r--r--activerecord/test/cases/associations/required_test.rb1
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb1
-rw-r--r--activerecord/test/cases/attributes_test.rb6
-rw-r--r--activerecord/test/cases/autosave_association_test.rb59
-rw-r--r--activerecord/test/cases/base_test.rb84
-rw-r--r--activerecord/test/cases/batches_test.rb2
-rw-r--r--activerecord/test/cases/bind_parameter_test.rb2
-rw-r--r--activerecord/test/cases/calculations_test.rb36
-rw-r--r--activerecord/test/cases/comment_test.rb25
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb15
-rw-r--r--activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb74
-rw-r--r--activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb3
-rw-r--r--activerecord/test/cases/connection_adapters/type_lookup_test.rb1
-rw-r--r--activerecord/test/cases/connection_pool_test.rb23
-rw-r--r--activerecord/test/cases/database_statements_test.rb1
-rw-r--r--activerecord/test/cases/explain_test.rb1
-rw-r--r--activerecord/test/cases/finder_respond_to_test.rb1
-rw-r--r--activerecord/test/cases/finder_test.rb14
-rw-r--r--activerecord/test/cases/fixtures_test.rb2
-rw-r--r--activerecord/test/cases/helper.rb1
-rw-r--r--activerecord/test/cases/hot_compatibility_test.rb1
-rw-r--r--activerecord/test/cases/inheritance_test.rb4
-rw-r--r--activerecord/test/cases/insert_all_test.rb1
-rw-r--r--activerecord/test/cases/json_serialization_test.rb3
-rw-r--r--activerecord/test/cases/locking_test.rb1
-rw-r--r--activerecord/test/cases/migration/compatibility_test.rb29
-rw-r--r--activerecord/test/cases/migration/create_join_table_test.rb1
-rw-r--r--activerecord/test/cases/migration/helper.rb1
-rw-r--r--activerecord/test/cases/migration/logger_test.rb9
-rw-r--r--activerecord/test/cases/migration/references_statements_test.rb1
-rw-r--r--activerecord/test/cases/migration_test.rb39
-rw-r--r--activerecord/test/cases/migrator_test.rb143
-rw-r--r--activerecord/test/cases/multi_db_migrator_test.rb218
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb1
-rw-r--r--activerecord/test/cases/pooled_connections_test.rb1
-rw-r--r--activerecord/test/cases/query_cache_test.rb24
-rw-r--r--activerecord/test/cases/relation/where_clause_test.rb1
-rw-r--r--activerecord/test/cases/relation_test.rb8
-rw-r--r--activerecord/test/cases/relations_test.rb32
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb1
-rw-r--r--activerecord/test/cases/schema_loading_test.rb1
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb1
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb3
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb3
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb1
-rw-r--r--activerecord/test/cases/transactions_test.rb1
-rw-r--r--activerecord/test/cases/unsafe_raw_sql_test.rb87
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb4
-rw-r--r--activerecord/test/cases/yaml_serialization_test.rb1
-rw-r--r--activerecord/test/models/author.rb1
-rw-r--r--activerecord/test/models/book.rb6
-rw-r--r--activerecord/test/models/club.rb1
-rw-r--r--activerecord/test/models/company.rb1
-rw-r--r--activerecord/test/models/company_in_module.rb1
-rw-r--r--activerecord/test/models/contact.rb18
-rw-r--r--activerecord/test/models/face.rb2
-rw-r--r--activerecord/test/models/mouse.rb6
-rw-r--r--activerecord/test/models/person.rb1
-rw-r--r--activerecord/test/models/ship.rb3
-rw-r--r--activerecord/test/models/squeak.rb6
-rw-r--r--activerecord/test/models/topic.rb1
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb4
-rw-r--r--activerecord/test/schema/schema.rb11
-rw-r--r--activerecord/test/support/config.rb1
246 files changed, 2017 insertions, 1520 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 8c5342edb2..727ddd6bb7 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,52 @@
+* Make currency symbols optional for money column type in PostgreSQL
+
+ *Joel Schneider*
+
+* Add support for beginless ranges, introduced in Ruby 2.7.
+
+ *Josh Goodall*
+
+* Add database_exists? method to connection adapters to check if a database exists.
+
+ *Guilherme Mansur*
+
+* Loading the schema for a model that has no `table_name` raises a `TableNotSpecified` error.
+
+ *Guilherme Mansur*, *Eugene Kenny*
+
+* PostgreSQL: Fix GROUP BY with ORDER BY virtual count attribute.
+
+ Fixes #36022.
+
+ *Ryuta Kamizono*
+
+* Make ActiveRecord `ConnectionPool.connections` method thread-safe.
+
+ Fixes #36465.
+
+ *Jeff Doering*
+
+* Add support for multiple databases to `rails db:abort_if_pending_migrations`.
+
+ *Mark Lee*
+
+* Fix sqlite3 collation parsing when using decimal columns.
+
+ *Martin R. Schuster*
+
+* Fix invalid schema when primary key column has a comment.
+
+ Fixes #29966.
+
+ *Guilherme Goettems Schneider*
+
+* Fix table comment also being applied to the primary key column.
+
+ *Guilherme Goettems Schneider*
+
* Allow generated `create_table` migrations to include or skip timestamps.
- *Michael Duchemin*
+ *Michael Duchemin*
+
Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activerecord/CHANGELOG.md) for previous changes.
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index 3250e29b82..aa08124158 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -14,7 +14,6 @@ module ActiveRecord
end
private
-
def clear_aggregation_cache
@aggregation_cache.clear if persisted?
end
diff --git a/activerecord/lib/active_record/association_relation.rb b/activerecord/lib/active_record/association_relation.rb
index 4c538ef2bd..de9892e48d 100644
--- a/activerecord/lib/active_record/association_relation.rb
+++ b/activerecord/lib/active_record/association_relation.rb
@@ -29,7 +29,6 @@ module ActiveRecord
end
private
-
def exec_queries
super do |record|
@association.set_inverse_instance_from_queries(record)
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index 272eede824..ac90ba0137 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -72,7 +72,6 @@ module ActiveRecord
attr_reader :aliases
private
-
def truncate(name)
name.slice(0, @connection.table_alias_length - 2)
end
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 0140aa15c8..6ad4c75fb5 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -46,7 +46,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
end
private
-
def self.suppress_composite_primary_key(pk)
pk unless pk.is_a?(Array)
end
@@ -73,7 +72,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
end
private
-
def middle_options(join_model)
middle_options = {}
middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index c3d4eab562..891b50d160 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -56,7 +56,7 @@ module ActiveRecord
def ids_writer(ids)
primary_key = reflection.association_primary_key
pk_type = klass.type_for_attribute(primary_key)
- ids = Array(ids).reject(&:blank?)
+ ids = Array(ids).compact_blank
ids.map! { |i| pk_type.cast(i) }
records = klass.where(primary_key => ids).index_by do |r|
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 85e0f076da..0db0ad8595 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -1029,7 +1029,7 @@ module ActiveRecord
alias_method :append, :<<
alias_method :concat, :<<
- def prepend(*args)
+ def prepend(*args) # :nodoc:
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
end
@@ -1101,7 +1101,6 @@ module ActiveRecord
delegate(*delegate_methods, to: :scope)
private
-
def find_nth_with_limit(index, limit)
load_target if find_from_target?
super
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 5972846940..dd2ed55279 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -37,7 +37,6 @@ module ActiveRecord
end
private
-
# Returns the number of records in this collection.
#
# If the association has a counter cache it gets that value. Otherwise
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 6b57e5093a..d4e8b364e1 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -95,7 +95,6 @@ module ActiveRecord
end
private
-
# Loads all the given data into +records+ for the +association+.
def preloaders_on(association, records, scope, polymorphic_parent = false)
case association
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 342d9e7a5a..4c7b0e6f07 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -27,7 +27,9 @@ module ActiveRecord
end
def records_by_owner
- @records_by_owner ||= preloaded_records.each_with_object({}) do |record, result|
+ # owners can be duplicated when a relation has a collection association join
+ # #compare_by_identity makes such owners different hash keys
+ @records_by_owner ||= preloaded_records.each_with_object({}.compare_by_identity) do |record, result|
owners_by_key[convert_key(record[association_key_name])].each do |owner|
(result[owner] ||= []) << record
end
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index 929045f29b..acb8ba7e5a 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -7,7 +7,6 @@ module ActiveRecord
include ActiveModel::AttributeAssignment
private
-
def _assign_attributes(attributes)
multi_parameter_attributes = {}
nested_parameter_attributes = {}
diff --git a/activerecord/lib/active_record/attribute_decorators.rb b/activerecord/lib/active_record/attribute_decorators.rb
index 98b7805c0a..0b66043d2a 100644
--- a/activerecord/lib/active_record/attribute_decorators.rb
+++ b/activerecord/lib/active_record/attribute_decorators.rb
@@ -46,7 +46,6 @@ module ActiveRecord
end
private
-
def load_schema!
super
attribute_types.each do |name, type|
@@ -75,7 +74,6 @@ module ActiveRecord
end
private
-
def decorators_for(name, type)
matching(name, type).map(&:last)
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 220043c061..21f72bb6c7 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -159,57 +159,6 @@ module ActiveRecord
end
end
- # Regexp for column names (with or without a table name prefix). Matches
- # the following:
- # "#{table_name}.#{column_name}"
- # "#{column_name}"
- COLUMN_NAME = /\A(?:\w+\.)?\w+\z/i
-
- # Regexp for column names with order (with or without a table name
- # prefix, with or without various order modifiers). Matches the following:
- # "#{table_name}.#{column_name}"
- # "#{table_name}.#{column_name} #{direction}"
- # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
- # "#{table_name}.#{column_name} NULLS LAST"
- # "#{column_name}"
- # "#{column_name} #{direction}"
- # "#{column_name} #{direction} NULLS FIRST"
- # "#{column_name} NULLS LAST"
- COLUMN_NAME_WITH_ORDER = /
- \A
- (?:\w+\.)?
- \w+
- (?:\s+asc|\s+desc)?
- (?:\s+nulls\s+(?:first|last))?
- \z
- /ix
-
- def disallow_raw_sql!(args, permit: COLUMN_NAME) # :nodoc:
- unexpected = args.reject do |arg|
- Arel.arel_node?(arg) ||
- arg.to_s.split(/\s*,\s*/).all? { |part| permit.match?(part) }
- end
-
- return if unexpected.none?
-
- if allow_unsafe_raw_sql == :deprecated
- ActiveSupport::Deprecation.warn(
- "Dangerous query method (method whose arguments are used as raw " \
- "SQL) called with non-attribute argument(s): " \
- "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
- "arguments will be disallowed in Rails 6.1. This method should " \
- "not be called with user-provided values, such as request " \
- "parameters or model attributes. Known-safe values can be passed " \
- "by wrapping them in Arel.sql()."
- )
- else
- raise(ActiveRecord::UnknownAttributeReference,
- "Query method called with non-attribute argument(s): " +
- unexpected.map(&:inspect).join(", ")
- )
- end
- end
-
# Returns true if the given attribute exists, otherwise false.
#
# class Person < ActiveRecord::Base
@@ -437,7 +386,7 @@ module ActiveRecord
def attributes_for_update(attribute_names)
attribute_names &= self.class.column_names
attribute_names.delete_if do |name|
- readonly_attribute?(name)
+ self.class.readonly_attribute?(name)
end
end
@@ -460,10 +409,6 @@ module ActiveRecord
end
end
- def readonly_attribute?(name)
- self.class.readonly_attributes.include?(name)
- end
-
def pk_attribute?(name)
name == @primary_key
end
diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
index 3d917ec9b1..4a7b6c60e5 100644
--- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
+++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
@@ -66,7 +66,6 @@ module ActiveRecord
end
private
-
# Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
def attribute_before_type_cast(attribute_name)
read_attribute_before_type_cast(attribute_name)
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index b4f5e6e75a..768c5f8c05 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -45,7 +45,6 @@ module ActiveRecord
end
private
-
def attribute_method?(attr_name)
attr_name == "id" || super
end
@@ -120,7 +119,6 @@ module ActiveRecord
end
private
-
def suppress_composite_primary_key(pk)
return pk unless pk.is_a?(Array)
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 0562327a9a..0f0e721b24 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -7,7 +7,6 @@ module ActiveRecord
module ClassMethods # :nodoc:
private
-
def define_method_attribute(name)
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
generated_attribute_methods, name
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 6e0e90f39c..7bc03b9eed 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -79,7 +79,6 @@ module ActiveRecord
end
private
-
def type_incompatible_with_serialize?(type, class_name)
type.is_a?(ActiveRecord::Type::Json) && class_name == ::JSON ||
type.respond_to?(:type_cast_array, true) && class_name == ::Array
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 294a3dc32c..fb44232dff 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -25,7 +25,6 @@ module ActiveRecord
end
private
-
def convert_time_to_time_zone(value)
return if value.nil?
@@ -64,7 +63,6 @@ module ActiveRecord
module ClassMethods # :nodoc:
private
-
def inherited(subclass)
super
# We need to apply this decorator here, rather than on module inclusion. The closure
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 1c63b553d0..66536a8ddf 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -11,7 +11,6 @@ module ActiveRecord
module ClassMethods # :nodoc:
private
-
def define_method_attribute=(name)
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
generated_attribute_methods, name, writer: true,
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
index 7cf421c184..c7846dbe7a 100644
--- a/activerecord/lib/active_record/attributes.rb
+++ b/activerecord/lib/active_record/attributes.rb
@@ -255,7 +255,6 @@ module ActiveRecord
end
private
-
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
private_constant :NO_DEFAULT_PROVIDED
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 8d89e7d84a..734ebb45ae 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -147,7 +147,6 @@ module ActiveRecord
module ClassMethods # :nodoc:
private
-
def define_non_cyclic_method(name, &block)
return if instance_methods(false).include?(name)
define_method(name) do |*args|
@@ -267,7 +266,6 @@ module ActiveRecord
end
private
-
# Returns the record for an association collection that should be validated
# or saved. If +autosave+ is +false+ only new records will be returned,
# unless the parent is/was a new record itself.
@@ -304,7 +302,7 @@ module ActiveRecord
def validate_single_association(reflection)
association = association_instance_get(reflection.name)
record = association && association.reader
- association_valid?(reflection, record) if record
+ association_valid?(reflection, record) if record && record.changed_for_autosave?
end
# Validate the associated records if <tt>:validate</tt> or
@@ -411,7 +409,7 @@ module ActiveRecord
saved = record.save(validate: false)
end
- raise ActiveRecord::Rollback unless saved
+ raise(RecordInvalid.new(association.owner)) unless saved
end
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 2af6d09b53..282c9fcf30 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -12,7 +12,6 @@ require "active_support/core_ext/hash/slice"
require "active_support/core_ext/string/behavior"
require "active_support/core_ext/kernel/singleton_class"
require "active_support/core_ext/module/introspection"
-require "active_support/core_ext/object/duplicable"
require "active_support/core_ext/class/subclasses"
require "active_record/attribute_decorators"
require "active_record/define_callbacks"
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index ef5444dfc3..a9ab9ab7a9 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -323,7 +323,6 @@ module ActiveRecord
end
private
-
def create_or_update(**)
_run_save_callbacks { super }
end
diff --git a/activerecord/lib/active_record/coders/yaml_column.rb b/activerecord/lib/active_record/coders/yaml_column.rb
index 11559141c7..881f0bcdb0 100644
--- a/activerecord/lib/active_record/coders/yaml_column.rb
+++ b/activerecord/lib/active_record/coders/yaml_column.rb
@@ -39,7 +39,6 @@ module ActiveRecord
end
private
-
def check_arity_of_constructor
load(nil)
rescue ArgumentError
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 4ff3cb0071..36001efdd5 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -3,6 +3,7 @@
require "thread"
require "concurrent/map"
require "monitor"
+require "weakref"
module ActiveRecord
# Raised when a connection could not be obtained within the connection
@@ -19,6 +20,26 @@ module ActiveRecord
end
module ConnectionAdapters
+ module AbstractPool # :nodoc:
+ def get_schema_cache(connection)
+ @schema_cache ||= SchemaCache.new(connection)
+ @schema_cache.connection = connection
+ @schema_cache
+ end
+
+ def set_schema_cache(cache)
+ @schema_cache = cache
+ end
+ end
+
+ class NullPool # :nodoc:
+ include ConnectionAdapters::AbstractPool
+
+ def initialize
+ @schema_cache = nil
+ end
+ end
+
# Connection pool base class for managing Active Record database
# connections.
#
@@ -146,7 +167,6 @@ module ActiveRecord
end
private
-
def internal_poll(timeout)
no_wait_poll || (timeout && wait_poll(timeout))
end
@@ -294,28 +314,36 @@ module ActiveRecord
@frequency = frequency
end
- @@mutex = Mutex.new
- @@pools = {}
+ @mutex = Mutex.new
+ @pools = {}
- def self.register_pool(pool, frequency) # :nodoc:
- @@mutex.synchronize do
- if @@pools.key?(frequency)
- @@pools[frequency] << pool
- else
- @@pools[frequency] = [pool]
+ class << self
+ def register_pool(pool, frequency) # :nodoc:
+ @mutex.synchronize do
+ unless @pools.key?(frequency)
+ @pools[frequency] = []
+ spawn_thread(frequency)
+ end
+ @pools[frequency] << WeakRef.new(pool)
+ end
+ end
+
+ private
+ def spawn_thread(frequency)
Thread.new(frequency) do |t|
loop do
sleep t
- @@mutex.synchronize do
- @@pools[frequency].each do |p|
+ @mutex.synchronize do
+ @pools[frequency].select!(&:weakref_alive?)
+ @pools[frequency].each do |p|
p.reap
p.flush
+ rescue WeakRef::RefError
end
end
end
end
end
- end
end
def run
@@ -326,9 +354,10 @@ module ActiveRecord
include MonitorMixin
include QueryCache::ConnectionPoolConfiguration
+ include ConnectionAdapters::AbstractPool
attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
- attr_reader :spec, :connections, :size, :reaper
+ attr_reader :spec, :size, :reaper
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
# object which describes database connection information (e.g. adapter,
@@ -397,7 +426,7 @@ module ActiveRecord
# #connection can be called any number of times; the connection is
# held in a cache keyed by a thread.
def connection
- @thread_cached_conns[connection_cache_key(@lock_thread || Thread.current)] ||= checkout
+ @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
end
# Returns true if there is an open connection being used for the current thread.
@@ -406,7 +435,7 @@ module ActiveRecord
# #connection or #with_connection methods. Connections obtained through
# #checkout will not be detected by #active_connection?
def active_connection?
- @thread_cached_conns[connection_cache_key(Thread.current)]
+ @thread_cached_conns[connection_cache_key(current_thread)]
end
# Signal that the thread is finished with the current connection.
@@ -441,6 +470,21 @@ module ActiveRecord
synchronize { @connections.any? }
end
+ # Returns an array containing the connections currently in the pool.
+ # Access to the array does not require synchronization on the pool because
+ # the array is newly created and not retained by the pool.
+ #
+ # However; this method bypasses the ConnectionPool's thread-safe connection
+ # access pattern. A returned connection may be owned by another thread,
+ # unowned, or by happen-stance owned by the calling thread.
+ #
+ # Calling methods on a connection without ownership is subject to the
+ # thread-safety guarantees of the underlying method. Many of the methods
+ # on connection adapter classes are inherently multi-thread unsafe.
+ def connections
+ synchronize { @connections.dup }
+ end
+
# Disconnects all connections in the pool, and clears the pool.
#
# Raises:
@@ -686,6 +730,10 @@ module ActiveRecord
thread
end
+ def current_thread
+ @lock_thread || Thread.current
+ end
+
# Take control of all existing connections so a "group" action such as
# reload/disconnect can be performed safely. It is no longer enough to
# wrap it in +synchronize+ because some pool's actions are allowed
@@ -827,7 +875,6 @@ module ActiveRecord
def new_connection
Base.send(spec.adapter_method, spec.config).tap do |conn|
- conn.schema_cache = schema_cache.dup if schema_cache
conn.check_version
end
end
@@ -956,15 +1003,30 @@ module ActiveRecord
end
end
+ attr_reader :prevent_writes
+
def initialize
# These caches are keyed by spec.name (ConnectionSpecification#name).
@owner_to_pool = ConnectionHandler.create_owner_to_pool
+ @prevent_writes = false
# Backup finalizer: if the forked child never needed a pool, the above
# early discard has not occurred
ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
end
+ # Prevent writing to the database regardless of role.
+ #
+ # In some cases you may want to prevent writes to the database
+ # even if you are on a database that can write. `while_preventing_writes`
+ # will prevent writes to the database for the duration of the block.
+ def while_preventing_writes
+ original, @prevent_writes = @prevent_writes, true
+ yield
+ ensure
+ @prevent_writes = original
+ end
+
def connection_pool_list
owner_to_pool.values.compact
end
@@ -1082,7 +1144,6 @@ module ActiveRecord
end
private
-
def owner_to_pool
@owner_to_pool[Process.pid]
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
index 75e959045e..d932f068f2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "active_support/deprecation"
-
module ActiveRecord
module ConnectionAdapters # :nodoc:
module DatabaseLimits
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index a7753e3e9c..768122b4d2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -33,17 +33,17 @@ module ActiveRecord
end
def enable_query_cache!
- @query_cache_enabled[connection_cache_key(Thread.current)] = true
+ @query_cache_enabled[connection_cache_key(current_thread)] = true
connection.enable_query_cache! if active_connection?
end
def disable_query_cache!
- @query_cache_enabled.delete connection_cache_key(Thread.current)
+ @query_cache_enabled.delete connection_cache_key(current_thread)
connection.disable_query_cache! if active_connection?
end
def query_cache_enabled
- @query_cache_enabled[connection_cache_key(Thread.current)]
+ @query_cache_enabled[connection_cache_key(current_thread)]
end
end
@@ -109,7 +109,6 @@ module ActiveRecord
end
private
-
def cache_sql(sql, name, binds)
@lock.synchronize do
result =
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 2877530917..93273f6cf6 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -114,16 +114,16 @@ module ActiveRecord
# if the value is a Time responding to usec.
def quoted_date(value)
if value.acts_like?(:time)
- zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
-
- if value.respond_to?(zone_conversion_method)
- value = value.send(zone_conversion_method)
+ if ActiveRecord::Base.default_timezone == :utc
+ value = value.getutc if value.respond_to?(:getutc) && !value.utc?
+ else
+ value = value.getlocal if value.respond_to?(:getlocal)
end
end
result = value.to_s(:db)
if value.respond_to?(:usec) && value.usec > 0
- "#{result}.#{sprintf("%06d", value.usec)}"
+ result << "." << sprintf("%06d", value.usec)
else
result
end
@@ -142,6 +142,59 @@ module ActiveRecord
value.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
end
+ def column_name_matcher # :nodoc:
+ COLUMN_NAME
+ end
+
+ def column_name_with_order_matcher # :nodoc:
+ COLUMN_NAME_WITH_ORDER
+ end
+
+ # Regexp for column names (with or without a table name prefix).
+ # Matches the following:
+ #
+ # "#{table_name}.#{column_name}"
+ # "#{column_name}"
+ COLUMN_NAME = /
+ \A
+ (
+ (?:
+ # table_name.column_name | function(one or no argument)
+ ((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
+ )
+ (?:(?:\s+AS)?\s+\w+)?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ # Regexp for column names with order (with or without a table name prefix,
+ # with or without various order modifiers). Matches the following:
+ #
+ # "#{table_name}.#{column_name}"
+ # "#{table_name}.#{column_name} #{direction}"
+ # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
+ # "#{table_name}.#{column_name} NULLS LAST"
+ # "#{column_name}"
+ # "#{column_name} #{direction}"
+ # "#{column_name} #{direction} NULLS FIRST"
+ # "#{column_name} NULLS LAST"
+ COLUMN_NAME_WITH_ORDER = /
+ \A
+ (
+ (?:
+ # table_name.column_name | function(one or no argument)
+ ((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
+ )
+ (?:\s+ASC|\s+DESC)?
+ (?:\s+NULLS\s+(?:FIRST|LAST))?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
+
private
def type_casted_binds(binds)
if binds.first.is_a?(Array)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
index 7d20825a75..23c993cfc3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
@@ -19,7 +19,6 @@ module ActiveRecord
to: :@conn, private: true
private
-
def visit_AlterTable(o)
sql = +"ALTER TABLE #{quote_table_name(o.name)} "
sql << o.adds.map { |col| accept col }.join(" ")
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 688eea75e8..dbd533b4b3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -264,8 +264,7 @@ module ActiveRecord
if_not_exists: false,
options: nil,
as: nil,
- comment: nil,
- **
+ comment: nil
)
@conn = conn
@columns_hash = {}
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index 622e00fffb..fb56e712be 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -15,7 +15,7 @@ module ActiveRecord
def column_spec_for_primary_key(column)
return {} if default_primary_key?(column)
spec = { id: schema_type(column).inspect }
- spec.merge!(prepare_column_options(column).except!(:null))
+ spec.merge!(prepare_column_options(column).except!(:null, :comment))
spec[:default] ||= "nil" if explicit_primary_key_default?(column)
spec
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index f97842b3f5..88367c79a1 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -2,7 +2,6 @@
require "active_record/migration/join_table"
require "active_support/core_ext/string/access"
-require "active_support/deprecation"
require "digest/sha2"
module ActiveRecord
@@ -101,7 +100,7 @@ module ActiveRecord
def index_exists?(table_name, column_name, options = {})
column_names = Array(column_name).map(&:to_s)
checks = []
- checks << lambda { |i| i.columns == column_names }
+ checks << lambda { |i| Array(i.columns) == column_names }
checks << lambda { |i| i.unique } if options[:unique]
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
@@ -291,25 +290,27 @@ module ActiveRecord
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
#
# See also TableDefinition#column for details on how to create columns.
- def create_table(table_name, **options)
- td = create_table_definition(table_name, options)
+ def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
+ td = create_table_definition(
+ table_name, options.extract!(:temporary, :if_not_exists, :options, :as, :comment)
+ )
- if options[:id] != false && !options[:as]
- pk = options.fetch(:primary_key) do
- Base.get_primary_key table_name.to_s.singularize
- end
+ if id && !td.as
+ pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
if pk.is_a?(Array)
td.primary_keys pk
else
- td.primary_key pk, options.fetch(:id, :primary_key), options
+ td.primary_key pk, id, options
end
end
yield td if block_given?
- if options[:force]
- drop_table(table_name, options.merge(if_exists: true))
+ if force
+ drop_table(table_name, force: force, if_exists: true)
+ else
+ schema_cache.clear_data_source_cache!(table_name.to_s)
end
result = execute schema_creation.accept td
@@ -321,7 +322,7 @@ module ActiveRecord
end
if supports_comments? && !supports_comments_in_create?
- if table_comment = options[:comment].presence
+ if table_comment = td.comment.presence
change_table_comment(table_name, table_comment)
end
@@ -499,6 +500,7 @@ module ActiveRecord
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
# In that case, +options+ and the block will be used by #create_table.
def drop_table(table_name, options = {})
+ schema_cache.clear_data_source_cache!(table_name.to_s)
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
end
@@ -518,14 +520,15 @@ module ActiveRecord
# Available options are (none of these exists by default):
# * <tt>:limit</tt> -
# Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
- # and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
# This option is ignored by some backends.
# * <tt>:default</tt> -
# The column's default value. Use +nil+ for +NULL+.
# * <tt>:null</tt> -
# Allows or disallows +NULL+ values in the column.
# * <tt>:precision</tt> -
- # Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
+ # Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
+ # <tt>:datetime</tt>, and <tt>:time</tt> columns.
# * <tt>:scale</tt> -
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
# * <tt>:collation</tt> -
@@ -1060,8 +1063,8 @@ module ActiveRecord
options
end
- def dump_schema_information #:nodoc:
- versions = ActiveRecord::SchemaMigration.all_versions
+ def dump_schema_information # :nodoc:
+ versions = schema_migration.all_versions
insert_versions_sql(versions) if versions.any?
end
@@ -1077,7 +1080,7 @@ module ActiveRecord
end
version = version.to_i
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
+ sm_table = quote_table_name(schema_migration.table_name)
migrated = migration_context.get_all_versions
versions = migration_context.migrations.map(&:version)
@@ -1450,7 +1453,7 @@ module ActiveRecord
end
def insert_versions_sql(versions)
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
+ sm_table = quote_table_name(schema_migration.table_name)
if versions.is_a?(Array)
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index 7b6321d83b..53ce8df491 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -5,10 +5,11 @@ module ActiveRecord
class TransactionState
def initialize(state = nil)
@state = state
- @children = []
+ @children = nil
end
def add_child(state)
+ @children ||= []
@children << state
end
@@ -41,12 +42,12 @@ module ActiveRecord
end
def rollback!
- @children.each { |c| c.rollback! }
+ @children&.each { |c| c.rollback! }
@state = :rolledback
end
def full_rollback!
- @children.each { |c| c.rollback! }
+ @children&.each { |c| c.rollback! }
@state = :fully_rolledback
end
@@ -75,18 +76,19 @@ module ActiveRecord
class Transaction #:nodoc:
attr_reader :connection, :state, :records, :savepoint_name, :isolation_level
- def initialize(connection, options, run_commit_callbacks: false)
+ def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
@connection = connection
@state = TransactionState.new
- @records = []
- @isolation_level = options[:isolation]
+ @records = nil
+ @isolation_level = isolation
@materialized = false
- @joinable = options.fetch(:joinable, true)
+ @joinable = joinable
@run_commit_callbacks = run_commit_callbacks
end
def add_record(record)
- records << record
+ @records ||= []
+ @records << record
end
def materialize!
@@ -98,6 +100,7 @@ module ActiveRecord
end
def rollback_records
+ return unless records
ite = records.uniq(&:object_id)
already_run_callbacks = {}
while record = ite.shift
@@ -107,16 +110,17 @@ module ActiveRecord
record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
end
ensure
- ite.each do |i|
+ ite&.each do |i|
i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
end
end
def before_commit_records
- records.uniq.each(&:before_committed!) if @run_commit_callbacks
+ records.uniq.each(&:before_committed!) if records && @run_commit_callbacks
end
def commit_records
+ return unless records
ite = records.uniq(&:object_id)
already_run_callbacks = {}
while record = ite.shift
@@ -131,7 +135,7 @@ module ActiveRecord
end
end
ensure
- ite.each { |i| i.committed!(should_run_callbacks: false) }
+ ite&.each { |i| i.committed!(should_run_callbacks: false) }
end
def full_rollback?; true; end
@@ -141,8 +145,8 @@ module ActiveRecord
end
class SavepointTransaction < Transaction
- def initialize(connection, savepoint_name, parent_transaction, *args)
- super(connection, *args)
+ def initialize(connection, savepoint_name, parent_transaction, **options)
+ super(connection, options)
parent_transaction.state.add_child(@state)
@@ -202,18 +206,29 @@ module ActiveRecord
@lazy_transactions_enabled = true
end
- def begin_transaction(options = {})
+ def begin_transaction(isolation: nil, joinable: true, _lazy: true)
@connection.lock.synchronize do
run_commit_callbacks = !current_transaction.joinable?
transaction =
if @stack.empty?
- RealTransaction.new(@connection, options, run_commit_callbacks: run_commit_callbacks)
+ RealTransaction.new(
+ @connection,
+ isolation: isolation,
+ joinable: joinable,
+ run_commit_callbacks: run_commit_callbacks
+ )
else
- SavepointTransaction.new(@connection, "active_record_#{@stack.size}", @stack.last, options,
- run_commit_callbacks: run_commit_callbacks)
+ SavepointTransaction.new(
+ @connection,
+ "active_record_#{@stack.size}",
+ @stack.last,
+ isolation: isolation,
+ joinable: joinable,
+ run_commit_callbacks: run_commit_callbacks
+ )
end
- if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && options[:_lazy] != false
+ if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && _lazy
@has_unmaterialized_transactions = true
else
transaction.materialize!
@@ -274,9 +289,9 @@ module ActiveRecord
end
end
- def within_new_transaction(options = {})
+ def within_new_transaction(isolation: nil, joinable: true)
@connection.lock.synchronize do
- transaction = begin_transaction options
+ transaction = begin_transaction(isolation: isolation, joinable: joinable)
yield
rescue Exception => error
if transaction
@@ -309,7 +324,6 @@ module ActiveRecord
end
private
-
NULL_TRANSACTION = NullTransaction.new
# Deallocate invalidated prepared statements outside of the transaction
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index bf0bb84c93..dc970c384b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -6,7 +6,6 @@ require "active_record/connection_adapters/sql_type_metadata"
require "active_record/connection_adapters/abstract/schema_dumper"
require "active_record/connection_adapters/abstract/schema_creation"
require "active_support/concurrency/load_interlock_aware_monitor"
-require "active_support/deprecation"
require "arel/collectors/bind"
require "arel/collectors/composite"
require "arel/collectors/sql_string"
@@ -78,7 +77,7 @@ module ActiveRecord
SIMPLE_INT = /\A\d+\z/
attr_accessor :pool
- attr_reader :schema_cache, :visitor, :owner, :logger, :lock, :prepared_statements, :prevent_writes
+ attr_reader :visitor, :owner, :logger, :lock, :prepared_statements
alias :in_use? :owner
set_callback :checkin, :after, :enable_lazy_transactions!
@@ -106,6 +105,14 @@ module ActiveRecord
Regexp.union(*parts)
end
+ def self.quoted_column_names # :nodoc:
+ @quoted_column_names ||= {}
+ end
+
+ def self.quoted_table_names # :nodoc:
+ @quoted_table_names ||= {}
+ end
+
def initialize(connection, logger = nil, config = {}) # :nodoc:
super()
@@ -114,11 +121,8 @@ module ActiveRecord
@instrumenter = ActiveSupport::Notifications.instrumenter
@logger = logger
@config = config
- @pool = nil
+ @pool = ActiveRecord::ConnectionAdapters::NullPool.new
@idle_since = Concurrent.monotonic_time
- @schema_cache = SchemaCache.new self
- @quoted_column_names, @quoted_table_names = {}, {}
- @prevent_writes = false
@visitor = arel_visitor
@statements = build_statement_pool
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
@@ -144,19 +148,7 @@ module ActiveRecord
# Returns true if the connection is a replica, or if +prevent_writes+
# is set to true.
def preventing_writes?
- replica? || prevent_writes
- end
-
- # Prevent writing to the database regardless of role.
- #
- # In some cases you may want to prevent writes to the database
- # even if you are on a database that can write. `while_preventing_writes`
- # will prevent writes to the database for the duration of the block.
- def while_preventing_writes
- original, @prevent_writes = @prevent_writes, true
- yield
- ensure
- @prevent_writes = original
+ replica? || ActiveRecord::Base.connection_handler.prevent_writes
end
def migrations_paths # :nodoc:
@@ -164,7 +156,22 @@ module ActiveRecord
end
def migration_context # :nodoc:
- MigrationContext.new(migrations_paths)
+ MigrationContext.new(migrations_paths, schema_migration)
+ end
+
+ def schema_migration # :nodoc:
+ @schema_migration ||= begin
+ conn = self
+ spec_name = conn.pool.spec.name
+ name = "#{spec_name}::SchemaMigration"
+
+ Class.new(ActiveRecord::SchemaMigration) do
+ define_singleton_method(:name) { name }
+ define_singleton_method(:to_s) { name }
+
+ self.connection_specification_name = spec_name
+ end
+ end
end
class Version
@@ -206,9 +213,13 @@ module ActiveRecord
@owner = Thread.current
end
+ def schema_cache
+ @pool.get_schema_cache(self)
+ end
+
def schema_cache=(cache)
cache.connection = self
- @schema_cache = cache
+ @pool.set_schema_cache(cache)
end
# this method must only be called while holding connection pool's mutex
@@ -259,6 +270,11 @@ module ActiveRecord
self.class::ADAPTER_NAME
end
+ # Does the database for this adapter exist?
+ def self.database_exists?(config)
+ raise NotImplementedError
+ end
+
# Does this adapter support DDL rollbacks in transactions? That is, would
# CREATE TABLE or ALTER TABLE get rolled back by a transaction?
def supports_ddl_transactions?
@@ -487,6 +503,9 @@ module ActiveRecord
#
# Prevent @connection's finalizer from touching the socket, or
# otherwise communicating with its server, when it is collected.
+ if schema_cache.connection == self
+ schema_cache.connection = nil
+ end
end
# Reset the state of this connection, directing the DBMS to clear
@@ -587,7 +606,6 @@ module ActiveRecord
end
private
-
def type_map
@type_map ||= Type::TypeMap.new.tap do |mapping|
initialize_type_map(mapping)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index ef33d712ad..ef1eef6b69 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -45,7 +45,6 @@ module ActiveRecord
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
private
-
def dealloc(stmt)
stmt.close
end
@@ -289,6 +288,8 @@ module ActiveRecord
# Example:
# rename_table('octopuses', 'octopi')
def rename_table(table_name, new_name)
+ schema_cache.clear_data_source_cache!(table_name.to_s)
+ schema_cache.clear_data_source_cache!(new_name.to_s)
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
rename_table_indexes(table_name, new_name)
end
@@ -309,6 +310,7 @@ module ActiveRecord
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
# In that case, +options+ and the block will be used by create_table.
def drop_table(table_name, options = {})
+ schema_cache.clear_data_source_cache!(table_name.to_s)
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
end
@@ -474,12 +476,12 @@ module ActiveRecord
# distinct queries, and requires that the ORDER BY include the distinct column.
# See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
def columns_for_distinct(columns, orders) # :nodoc:
- order_columns = orders.reject(&:blank?).map { |s|
+ order_columns = orders.compact_blank.map { |s|
# Convert Arel node to string
s = s.to_sql unless s.is_a?(String)
# Remove any ASC/DESC modifiers
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
+ }.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
(order_columns << super).join(", ")
end
@@ -513,7 +515,6 @@ module ActiveRecord
end
private
-
def initialize_type_map(m = type_map)
super
@@ -618,7 +619,11 @@ module ActiveRecord
when ER_QUERY_INTERRUPTED
QueryCanceled.new(message, sql: sql, binds: binds)
else
- super
+ if exception.is_a?(Mysql2::Error::TimeoutError)
+ ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds)
+ else
+ super
+ end
end
end
@@ -736,7 +741,7 @@ module ActiveRecord
end.compact.join(", ")
# ...and send them all in one query
- execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
+ execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
end
def column_definitions(table_name) # :nodoc:
@@ -795,7 +800,6 @@ module ActiveRecord
end
private
-
def cast_value(value)
case value
when true then "1"
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 279d0b9e84..2708d2756b 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -5,6 +5,8 @@ module ActiveRecord
module ConnectionAdapters
# An abstract definition of a column in a table.
class Column
+ include Deduplicable
+
attr_reader :name, :default, :sql_type_metadata, :null, :default_function, :collation, :comment
delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
@@ -76,6 +78,7 @@ module ActiveRecord
def hash
Column.hash ^
name.hash ^
+ name.encoding.hash ^
default.hash ^
sql_type_metadata.hash ^
null.hash ^
@@ -83,6 +86,17 @@ module ActiveRecord
collation.hash ^
comment.hash
end
+
+ private
+ def deduplicated
+ @name = -name
+ @sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
+ @default = -default if default
+ @default_function = -default_function if default_function
+ @collation = -collation if collation
+ @comment = -comment if comment
+ super
+ end
end
class NullColumn < Column
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 9eaf9d9a89..0732b69f81 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -50,13 +50,12 @@ module ActiveRecord
# Converts the given URL to a full connection hash.
def to_hash
- config = raw_config.reject { |_, value| value.blank? }
+ config = raw_config.compact_blank
config.map { |key, value| config[key] = uri_parser.unescape(value) if value.is_a? String }
config
end
private
-
attr_reader :uri
def uri_parser
diff --git a/activerecord/lib/active_record/connection_adapters/deduplicable.rb b/activerecord/lib/active_record/connection_adapters/deduplicable.rb
new file mode 100644
index 0000000000..fb2fd60bbc
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/deduplicable.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module ActiveRecord
+ module ConnectionAdapters # :nodoc:
+ module Deduplicable
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def registry
+ @registry ||= {}
+ end
+
+ def new(*)
+ super.deduplicate
+ end
+ end
+
+ def deduplicate
+ self.class.registry[self] ||= deduplicated
+ end
+ alias :-@ :deduplicate
+
+ private
+ def deduplicated
+ freeze
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb b/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb
index 1df4dea2d8..97d74df529 100644
--- a/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb
+++ b/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb
@@ -5,7 +5,7 @@ module ActiveRecord
module DetermineIfPreparableVisitor
attr_accessor :preparable
- def accept(*)
+ def accept(object, collector)
@preparable = true
super
end
@@ -20,7 +20,7 @@ module ActiveRecord
super
end
- def visit_Arel_Nodes_SqlLiteral(*)
+ def visit_Arel_Nodes_SqlLiteral(o, collector)
@preparable = false
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb b/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb
index 20c3c83664..edd5ea0542 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb
@@ -37,7 +37,6 @@ module ActiveRecord
end
private
-
def compute_column_widths(result)
[].tap do |widths|
result.columns.each_with_index do |column, i|
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
index 75564a61d6..0069f5871c 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
@@ -5,11 +5,11 @@ module ActiveRecord
module MySQL
module Quoting # :nodoc:
def quote_column_name(name)
- @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
+ self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
end
def quote_table_name(name)
- @quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
+ self.class.quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
end
def unquoted_true
@@ -32,12 +32,49 @@ module ActiveRecord
"x'#{value.hex}'"
end
- def _type_cast(value)
- case value
- when Date, Time then value
- else super
- end
+ def column_name_matcher
+ COLUMN_NAME
+ end
+
+ def column_name_with_order_matcher
+ COLUMN_NAME_WITH_ORDER
end
+
+ COLUMN_NAME = /
+ \A
+ (
+ (?:
+ # `table_name`.`column_name` | function(one or no argument)
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
+ )
+ (?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ COLUMN_NAME_WITH_ORDER = /
+ \A
+ (
+ (?:
+ # `table_name`.`column_name` | function(one or no argument)
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
+ )
+ (?:\s+ASC|\s+DESC)?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
+
+ private
+ def _type_cast(value)
+ case value
+ when Date, Time then value
+ else super
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
index 82ed320617..0f5ab7562a 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
@@ -7,7 +7,6 @@ module ActiveRecord
delegate :add_sql_comment!, :mariadb?, to: :@conn, private: true
private
-
def visit_DropForeignKey(name)
"DROP FOREIGN KEY #{name}"
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
index 234fb25fdf..bcd300f3db 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
@@ -41,13 +41,15 @@ module ActiveRecord
case column.sql_type
when /\Atimestamp\b/
:timestamp
+ when /\A(?:enum|set)\b/
+ column.sql_type
else
super
end
end
def schema_limit(column)
- super unless /\A(?:tiny|medium|long)?(?:text|blob)/.match?(column.sql_type)
+ super unless /\A(?:enum|set|(?:tiny|medium|long)?(?:text|blob))\b/.match?(column.sql_type)
end
def schema_precision(column)
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
index 9167593064..a7232fa249 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
@@ -6,9 +6,11 @@ module ActiveRecord
class TypeMetadata < DelegateClass(SqlTypeMetadata) # :nodoc:
undef to_yaml if method_defined?(:to_yaml)
+ include Deduplicable
+
attr_reader :extra
- def initialize(type_metadata, extra: "")
+ def initialize(type_metadata, extra: nil)
super(type_metadata)
@extra = extra
end
@@ -25,6 +27,13 @@ module ActiveRecord
__getobj__.hash ^
extra.hash
end
+
+ private
+ def deduplicated
+ __setobj__(__getobj__.deduplicate)
+ @extra = -extra if extra
+ super
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 5b0335c22b..1df9ac32c9 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -8,6 +8,8 @@ require "mysql2"
module ActiveRecord
module ConnectionHandling # :nodoc:
+ ER_BAD_DB_ERROR = 1049
+
# Establishes a connection to the database that's used by all Active Record objects.
def mysql2_connection(config)
config = config.symbolize_keys
@@ -22,7 +24,7 @@ module ActiveRecord
client = Mysql2::Client.new(config)
ConnectionAdapters::Mysql2Adapter.new(client, logger, nil, config)
rescue Mysql2::Error => error
- if error.message.include?("Unknown database")
+ if error.error_number == ER_BAD_DB_ERROR
raise ActiveRecord::NoDatabaseError
else
raise
@@ -42,6 +44,12 @@ module ActiveRecord
configure_connection
end
+ def self.database_exists?(config)
+ !!ActiveRecord::Base.mysql2_connection(config)
+ rescue ActiveRecord::NoDatabaseError
+ false
+ end
+
def supports_json?
!mariadb? && database_version >= "5.7.8"
end
@@ -109,12 +117,12 @@ module ActiveRecord
end
def discard! # :nodoc:
+ super
@connection.automatic_close = false
@connection = nil
end
private
-
def connect
@connection = Mysql2::Client.new(@config)
configure_connection
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
index ec25bb1e19..f1ecf6df30 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
@@ -23,6 +23,29 @@ module ActiveRecord
def sql_type
super.sub(/\[\]\z/, "")
end
+
+ def init_with(coder)
+ @serial = coder["serial"]
+ super
+ end
+
+ def encode_with(coder)
+ coder["serial"] = @serial
+ super
+ end
+
+ def ==(other)
+ other.is_a?(Column) &&
+ super &&
+ serial? == other.serial?
+ end
+ alias :eql? :==
+
+ def hash
+ Column.hash ^
+ super.hash ^
+ serial?.hash
+ end
end
end
PostgreSQLColumn = PostgreSQL::Column # :nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
index b1dfbde86e..0bbe98145a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -77,7 +77,6 @@ module ActiveRecord
end
private
-
def type_cast_array(value, method)
if value.is_a?(::Array)
value.map { |item| type_cast_array(item, method) }
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb
index f70f09ad95..bae34472e1 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb
@@ -10,7 +10,6 @@ module ActiveRecord
end
private
-
def cast_value(value)
value.to_s
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
index 7b42677101..8d4dacbd64 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
@@ -46,7 +46,6 @@ module ActiveRecord
end
private
-
HstorePair = begin
quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb
index 7f6adc351c..e52d4385ef 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb
@@ -34,7 +34,6 @@ module ActiveRecord
end
private
-
def number_for_point(number)
number.to_s.gsub(/\.0$/, "")
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
index 6434377b57..357493dfc0 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
@@ -26,9 +26,9 @@ module ActiveRecord
value = value.sub(/^\((.+)\)$/, '-\1') # (4)
case value
- when /^-?\D+[\d,]+\.\d{2}$/ # (1)
+ when /^-?\D*[\d,]+\.\d{2}$/ # (1)
value.gsub!(/[^-\d.]/, "")
- when /^-?\D+[\d.]+,\d{2}$/ # (2)
+ when /^-?\D*[\d.]+,\d{2}$/ # (2)
value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
index 8c74cecc4d..e81e18ff70 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
@@ -50,7 +50,6 @@ module ActiveRecord
end
private
-
def number_for_point(number)
number.to_s.gsub(/\.0$/, "")
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
index aa7701e038..d19f1f9cf8 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
@@ -58,7 +58,6 @@ module ActiveRecord
end
private
-
def type_cast_single(value)
infinity?(value) ? value : @subtype.deserialize(value)
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
index 28abdbd073..74a28eef58 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
@@ -14,7 +14,6 @@ module ActiveRecord
end
private
-
def cast_value(value)
casted = value.to_s
casted if casted.match?(ACCEPTABLE_UUID)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index d40e0ef1f0..07b66de366 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -30,7 +30,7 @@ module ActiveRecord
# - "schema.name".table_name
# - "schema.name"."table.name"
def quote_table_name(name) # :nodoc:
- @quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
+ self.class.quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
end
# Quotes schema names for use in SQL queries.
@@ -44,7 +44,7 @@ module ActiveRecord
# Quotes column names for use in SQL queries.
def quote_column_name(name) # :nodoc:
- @quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
+ self.class.quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
end
# Quote date/time values for use in SQL input.
@@ -78,6 +78,43 @@ module ActiveRecord
type_map.lookup(column.oid, column.fmod, column.sql_type)
end
+ def column_name_matcher
+ COLUMN_NAME
+ end
+
+ def column_name_with_order_matcher
+ COLUMN_NAME_WITH_ORDER
+ end
+
+ COLUMN_NAME = /
+ \A
+ (
+ (?:
+ # "table_name"."column_name"::type_name | function(one or no argument)::type_name
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
+ )
+ (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ COLUMN_NAME_WITH_ORDER = /
+ \A
+ (
+ (?:
+ # "table_name"."column_name"::type_name | function(one or no argument)::type_name
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
+ )
+ (?:\s+ASC|\s+DESC)?
+ (?:\s+NULLS\s+(?:FIRST|LAST))?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
+
private
def lookup_cast_type(sql_type)
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
index 84643d20da..d201e40190 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
@@ -5,7 +5,6 @@ module ActiveRecord
module PostgreSQL
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
private
-
def extensions(stream)
extensions = @connection.extensions
if extensions.any?
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 40c5e51d92..628a609521 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -55,6 +55,7 @@ module ActiveRecord
end
def drop_table(table_name, options = {}) # :nodoc:
+ schema_cache.clear_data_source_cache!(table_name.to_s)
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
end
@@ -376,6 +377,8 @@ module ActiveRecord
# rename_table('octopuses', 'octopi')
def rename_table(table_name, new_name)
clear_cache!
+ schema_cache.clear_data_source_cache!(table_name.to_s)
+ schema_cache.clear_data_source_cache!(new_name.to_s)
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
pk, seq = pk_and_sequence_for(new_name)
if pk
@@ -552,13 +555,13 @@ module ActiveRecord
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
# requires that the ORDER BY include the distinct column.
def columns_for_distinct(columns, orders) #:nodoc:
- order_columns = orders.reject(&:blank?).map { |s|
+ order_columns = orders.compact_blank.map { |s|
# Convert Arel node to string
s = s.to_sql unless s.is_a?(String)
# Remove any ASC/DESC modifiers
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
+ }.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
(order_columns << super).join(", ")
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
index 8bdec623af..b7f6479357 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
@@ -7,6 +7,8 @@ module ActiveRecord
class TypeMetadata < DelegateClass(SqlTypeMetadata)
undef to_yaml if method_defined?(:to_yaml)
+ include Deduplicable
+
attr_reader :oid, :fmod
def initialize(type_metadata, oid: nil, fmod: nil)
@@ -29,6 +31,12 @@ module ActiveRecord
oid.hash ^
fmod.hash
end
+
+ private
+ def deduplicated
+ __setobj__(__getobj__.deduplicate)
+ super
+ end
end
end
PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
index f2f4701500..e8caeb8132 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
@@ -37,7 +37,6 @@ module ActiveRecord
end
protected
-
def parts
@parts ||= [@schema, @identifier].compact
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 91318a0af1..0a7c6d8ac4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -46,7 +46,7 @@ module ActiveRecord
conn = PG.connect(conn_params)
ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
rescue ::PG::Error => error
- if error.message.include?("does not exist")
+ if error.message.include?(conn_params[:dbname])
raise ActiveRecord::NoDatabaseError
else
raise
@@ -259,6 +259,12 @@ module ActiveRecord
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
end
+ def self.database_exists?(config)
+ !!ActiveRecord::Base.postgresql_connection(config)
+ rescue ActiveRecord::NoDatabaseError
+ false
+ end
+
# Is this connection alive and ready for queries?
def active?
@lock.synchronize do
@@ -302,6 +308,7 @@ module ActiveRecord
end
def discard! # :nodoc:
+ super
@connection.socket_io.reopen(IO::NULL) rescue nil
@connection = nil
end
@@ -452,7 +459,6 @@ module ActiveRecord
end
private
-
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
VALUE_LIMIT_VIOLATION = "22001"
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
index dbfe1e4a34..5e30304864 100644
--- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
@@ -27,7 +27,6 @@ module ActiveRecord
def encode_with(coder)
coder["columns"] = @columns
- coder["columns_hash"] = @columns_hash
coder["primary_keys"] = @primary_keys
coder["data_sources"] = @data_sources
coder["indexes"] = @indexes
@@ -37,16 +36,21 @@ module ActiveRecord
def init_with(coder)
@columns = coder["columns"]
- @columns_hash = coder["columns_hash"]
@primary_keys = coder["primary_keys"]
@data_sources = coder["data_sources"]
@indexes = coder["indexes"] || {}
@version = coder["version"]
@database_version = coder["database_version"]
+
+ derive_columns_hash_and_deduplicate_values
end
def primary_keys(table_name)
- @primary_keys[table_name] ||= data_source_exists?(table_name) ? connection.primary_key(table_name) : nil
+ @primary_keys.fetch(table_name) do
+ if data_source_exists?(table_name)
+ @primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
+ end
+ end
end
# A cached lookup for table existence.
@@ -54,7 +58,7 @@ module ActiveRecord
prepare_data_sources if @data_sources.empty?
return @data_sources[name] if @data_sources.key? name
- @data_sources[name] = connection.data_source_exists?(name)
+ @data_sources[deep_deduplicate(name)] = connection.data_source_exists?(name)
end
# Add internal cache for table with +table_name+.
@@ -73,15 +77,17 @@ module ActiveRecord
# Get the columns for a table
def columns(table_name)
- @columns[table_name] ||= connection.columns(table_name)
+ @columns.fetch(table_name) do
+ @columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
+ end
end
# Get the columns for a table as a hash, key is the column name
# value is the column object.
def columns_hash(table_name)
- @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
- [col.name, col]
- }]
+ @columns_hash.fetch(table_name) do
+ @columns_hash[deep_deduplicate(table_name)] = columns(table_name).index_by(&:name)
+ end
end
# Checks whether the columns hash is already cached for a table.
@@ -90,7 +96,9 @@ module ActiveRecord
end
def indexes(table_name)
- @indexes[table_name] ||= connection.indexes(table_name)
+ @indexes.fetch(table_name) do
+ @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
+ end
end
def database_version # :nodoc:
@@ -124,15 +132,38 @@ module ActiveRecord
def marshal_dump
# if we get current version during initialization, it happens stack over flow.
@version = connection.migration_context.current_version
- [@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, database_version]
+ [@version, @columns, {}, @primary_keys, @data_sources, @indexes, database_version]
end
def marshal_load(array)
- @version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
- @indexes = @indexes || {}
+ @version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
+ @indexes ||= {}
+
+ derive_columns_hash_and_deduplicate_values
end
private
+ def derive_columns_hash_and_deduplicate_values
+ @columns = deep_deduplicate(@columns)
+ @columns_hash = @columns.transform_values { |columns| columns.index_by(&:name) }
+ @primary_keys = deep_deduplicate(@primary_keys)
+ @data_sources = deep_deduplicate(@data_sources)
+ @indexes = deep_deduplicate(@indexes)
+ end
+
+ def deep_deduplicate(value)
+ case value
+ when Hash
+ value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
+ when Array
+ value.map { |i| deep_deduplicate(i) }
+ when String, Deduplicable
+ -value
+ else
+ value
+ end
+ end
+
def prepare_data_sources
connection.data_sources.each { |source| @data_sources[source] = true }
end
diff --git a/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb b/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb
index df28df7a7c..969867e70f 100644
--- a/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb
@@ -1,9 +1,13 @@
# frozen_string_literal: true
+require "active_record/connection_adapters/deduplicable"
+
module ActiveRecord
# :stopdoc:
module ConnectionAdapters
class SqlTypeMetadata
+ include Deduplicable
+
attr_reader :sql_type, :type, :limit, :precision, :scale
def initialize(sql_type: nil, type: nil, limit: nil, precision: nil, scale: nil)
@@ -32,6 +36,12 @@ module ActiveRecord
precision.hash >> 1 ^
scale.hash >> 2
end
+
+ private
+ def deduplicated
+ @sql_type = -sql_type
+ super
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
index cb9d32a577..9b74a774e5 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
@@ -13,11 +13,11 @@ module ActiveRecord
end
def quote_table_name(name)
- @quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
+ self.class.quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
end
def quote_column_name(name)
- @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
+ self.class.quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
end
def quoted_time(value)
@@ -45,8 +45,43 @@ module ActiveRecord
0
end
- private
+ def column_name_matcher
+ COLUMN_NAME
+ end
+
+ def column_name_with_order_matcher
+ COLUMN_NAME_WITH_ORDER
+ end
+ COLUMN_NAME = /
+ \A
+ (
+ (?:
+ # "table_name"."column_name" | function(one or no argument)
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
+ )
+ (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ COLUMN_NAME_WITH_ORDER = /
+ \A
+ (
+ (?:
+ # "table_name"."column_name" | function(one or no argument)
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
+ )
+ (?:\s+ASC|\s+DESC)?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
+
+ private
def _type_cast(value)
case value
when BigDecimal
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 7f3f32162e..f4847eb6c0 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -98,6 +98,16 @@ module ActiveRecord
configure_connection
end
+ def self.database_exists?(config)
+ config = config.symbolize_keys
+ if config[:database] == ":memory:"
+ return true
+ else
+ database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
+ File.exist?(database_file)
+ end
+ end
+
def supports_ddl_transactions?
true
end
@@ -218,6 +228,8 @@ module ActiveRecord
# Example:
# rename_table('octopuses', 'octopi')
def rename_table(table_name, new_name)
+ schema_cache.clear_data_source_cache!(table_name.to_s)
+ schema_cache.clear_data_source_cache!(new_name.to_s)
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
rename_table_indexes(table_name, new_name)
end
@@ -381,6 +393,7 @@ module ActiveRecord
if from_primary_key.is_a?(Array)
@definition.primary_keys from_primary_key
end
+
columns(from).each do |column|
column_name = options[:rename] ?
(options[:rename][column.name] ||
@@ -477,9 +490,9 @@ module ActiveRecord
result = exec_query(sql, "SCHEMA").first
if result
- # Splitting with left parentheses and picking up last will return all
+ # Splitting with left parentheses and discarding the first part will return all
# columns separated with comma(,).
- columns_string = result["sql"].split("(").last
+ columns_string = result["sql"].split("(", 2).last
columns_string.split(",").each do |column_string|
# This regex will match the column name and collation type and will save
diff --git a/activerecord/lib/active_record/connection_adapters/statement_pool.rb b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
index 46bd831da7..0960feed84 100644
--- a/activerecord/lib/active_record/connection_adapters/statement_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
@@ -48,7 +48,6 @@ module ActiveRecord
end
private
-
def cache
@cache[Process.pid]
end
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index 040ebdb960..c8cefa9906 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -173,7 +173,7 @@ module ActiveRecord
raise "Anonymous class is not allowed." unless name
config_or_env ||= DEFAULT_ENV.call.to_sym
- pool_name = self == Base ? "primary" : name
+ pool_name = primary_class? ? "primary" : name
self.connection_specification_name = pool_name
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
@@ -204,11 +204,15 @@ module ActiveRecord
# Return the specification name from the current class or its parent.
def connection_specification_name
if !defined?(@connection_specification_name) || @connection_specification_name.nil?
- return self == Base ? "primary" : superclass.connection_specification_name
+ return primary_class? ? "primary" : superclass.connection_specification_name
end
@connection_specification_name
end
+ def primary_class? # :nodoc:
+ self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
+ end
+
# Returns the configuration of the associated connection as a hash:
#
# ActiveRecord::Base.connection_config
@@ -252,7 +256,6 @@ module ActiveRecord
:clear_all_connections!, :flush_idle_connections!, to: :connection_handler
private
-
def swap_connection_handler(handler, &blk) # :nodoc:
old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
yield
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index dfd33d3dd7..595ef4ee25 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -286,7 +286,6 @@ module ActiveRecord
end
private
-
def cached_find_by_statement(key, &block)
cache = @find_by_statement_cache[connection.prepared_statements]
cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
@@ -554,7 +553,6 @@ module ActiveRecord
end
private
-
# +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
# the array, and then rescues from the possible +NoMethodError+. If those elements are
# +ActiveRecord::Base+'s, then this triggers the various +method_missing+'s that we have,
diff --git a/activerecord/lib/active_record/database_configurations.rb b/activerecord/lib/active_record/database_configurations.rb
index 44b5cfc738..e122628b05 100644
--- a/activerecord/lib/active_record/database_configurations.rb
+++ b/activerecord/lib/active_record/database_configurations.rb
@@ -104,18 +104,28 @@ module ActiveRecord
return configs.configurations if configs.is_a?(DatabaseConfigurations)
return configs if configs.is_a?(Array)
- build_db_config = configs.each_pair.flat_map do |env_name, config|
- walk_configs(env_name.to_s, "primary", config)
- end.flatten.compact
+ db_configs = configs.flat_map do |env_name, config|
+ if config.is_a?(Hash) && config.all? { |k, v| v.is_a?(Hash) }
+ walk_configs(env_name.to_s, config)
+ else
+ build_db_config_from_raw_config(env_name.to_s, "primary", config)
+ end
+ end.compact
if url = ENV["DATABASE_URL"]
- build_url_config(url, build_db_config)
+ merge_url_with_configs(url, db_configs)
else
- build_db_config
+ db_configs
+ end
+ end
+
+ def walk_configs(env_name, config)
+ config.map do |spec_name, sub_config|
+ build_db_config_from_raw_config(env_name, spec_name.to_s, sub_config)
end
end
- def walk_configs(env_name, spec_name, config)
+ def build_db_config_from_raw_config(env_name, spec_name, config)
case config
when String
build_db_config_from_string(env_name, spec_name, config)
@@ -141,23 +151,19 @@ module ActiveRecord
config_without_url.delete "url"
ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
- elsif config["database"] || (config.size == 1 && config.values.all? { |v| v.is_a? String })
- ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
else
- config.each_pair.map do |sub_spec_name, sub_config|
- walk_configs(env_name, sub_spec_name, sub_config)
- end
+ ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
end
end
- def build_url_config(url, configs)
+ def merge_url_with_configs(url, configs)
env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
- if original_config = configs.find(&:for_current_env?)
- if original_config.url_config?
- configs
- else
- configs.map do |config|
+ if configs.find(&:for_current_env?)
+ configs.map do |config|
+ if config.url_config?
+ config
+ else
ActiveRecord::DatabaseConfigurations::UrlConfig.new(config.env_name, config.spec_name, url, config.config)
end
end
diff --git a/activerecord/lib/active_record/database_configurations/url_config.rb b/activerecord/lib/active_record/database_configurations/url_config.rb
index e2d30ae416..e6b4acc647 100644
--- a/activerecord/lib/active_record/database_configurations/url_config.rb
+++ b/activerecord/lib/active_record/database_configurations/url_config.rb
@@ -56,7 +56,6 @@ module ActiveRecord
end
private
-
def build_url_hash(url)
if url.nil? || /^jdbc:/.match?(url)
{ "url" => url }
diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb
index 398a029068..7d9e221faa 100644
--- a/activerecord/lib/active_record/dynamic_matchers.rb
+++ b/activerecord/lib/active_record/dynamic_matchers.rb
@@ -69,7 +69,6 @@ module ActiveRecord
end
private
-
def body
"#{finder}(#{attributes_hash})"
end
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 60cf9818c1..20cc987d6e 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -38,6 +38,10 @@ module ActiveRecord
class AdapterNotSpecified < ActiveRecordError
end
+ # Raised when a model makes a query but it has not specified an associated table.
+ class TableNotSpecified < ActiveRecordError
+ end
+
# Raised when Active Record cannot find database adapter specified in
# +config/database.yml+ or programmatically.
class AdapterNotFound < ActiveRecordError
@@ -349,16 +353,24 @@ module ActiveRecord
class IrreversibleOrderError < ActiveRecordError
end
+ # Superclass for errors that have been aborted (either by client or server).
+ class QueryAborted < StatementInvalid
+ end
+
# LockWaitTimeout will be raised when lock wait timeout exceeded.
class LockWaitTimeout < StatementInvalid
end
# StatementTimeout will be raised when statement timeout exceeded.
- class StatementTimeout < StatementInvalid
+ class StatementTimeout < QueryAborted
end
# QueryCanceled will be raised when canceling statement due to user request.
- class QueryCanceled < StatementInvalid
+ class QueryCanceled < QueryAborted
+ end
+
+ # AdapterTimeout will be raised when database clients times out while waiting from the server.
+ class AdapterTimeout < QueryAborted
end
# UnknownAttributeReference is raised when an unknown and potentially unsafe
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
index 919e96cd7a..5dca75c539 100644
--- a/activerecord/lib/active_record/explain.rb
+++ b/activerecord/lib/active_record/explain.rb
@@ -36,7 +36,6 @@ module ActiveRecord
end
private
-
def render_bind(attr)
value = if attr.type.binary? && attr.value
"<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
diff --git a/activerecord/lib/active_record/fixture_set/table_row.rb b/activerecord/lib/active_record/fixture_set/table_row.rb
index cb4726f1ee..f65329f91d 100644
--- a/activerecord/lib/active_record/fixture_set/table_row.rb
+++ b/activerecord/lib/active_record/fixture_set/table_row.rb
@@ -48,7 +48,6 @@ module ActiveRecord
end
private
-
def model_metadata
@table_rows.model_metadata
end
diff --git a/activerecord/lib/active_record/fixture_set/table_rows.rb b/activerecord/lib/active_record/fixture_set/table_rows.rb
index 23814b6cb5..df1cd63963 100644
--- a/activerecord/lib/active_record/fixture_set/table_rows.rb
+++ b/activerecord/lib/active_record/fixture_set/table_rows.rb
@@ -29,7 +29,6 @@ module ActiveRecord
end
private
-
def build_table_rows_from(table_name, fixtures, config)
now = config.default_timezone == :utc ? Time.now.utc : Time.now
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 327121a2a2..046ed0e95c 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -464,7 +464,6 @@ module ActiveRecord
end
private
-
def insert_class(class_names, name, klass)
# We only want to deal with AR objects.
if klass && klass < ActiveRecord::Base
@@ -570,7 +569,6 @@ module ActiveRecord
end
private
-
def read_and_insert(fixtures_directory, fixture_files, class_names, connection) # :nodoc:
fixtures_map = {}
fixture_sets = fixture_files.map do |fixture_set_name|
@@ -661,7 +659,6 @@ module ActiveRecord
end
private
-
def model_class=(class_name)
if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
@model_class = class_name
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 9570bc6f86..5ca48fa18c 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -176,7 +176,6 @@ module ActiveRecord
end
protected
-
# Returns the class type of the record using the current module as a prefix. So descendants of
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
def compute_type(type_name)
@@ -208,7 +207,6 @@ module ActiveRecord
end
private
-
# Called by +instantiate+ to decide which class to use for a new
# record instance. For single-table inheritance, we check the record
# for a +type+ column and return the corresponding class.
@@ -272,7 +270,6 @@ module ActiveRecord
end
private
-
def initialize_internals_callback
super
ensure_proper_type
diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb
index 573a823dbc..4a97061731 100644
--- a/activerecord/lib/active_record/integration.rb
+++ b/activerecord/lib/active_record/integration.rb
@@ -93,7 +93,7 @@ module ActiveRecord
# cache_version, but this method can be overwritten to return something else.
#
# Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
- # +false+ (which it is by default until Rails 6.0).
+ # +false+.
def cache_version
return unless cache_versioning
diff --git a/activerecord/lib/active_record/internal_metadata.rb b/activerecord/lib/active_record/internal_metadata.rb
index e6166581f1..8f3c6d0ee3 100644
--- a/activerecord/lib/active_record/internal_metadata.rb
+++ b/activerecord/lib/active_record/internal_metadata.rb
@@ -28,10 +28,6 @@ module ActiveRecord
where(key: key).pluck(:value).first
end
- def table_exists?
- connection.table_exists?(table_name)
- end
-
# Creates an internal metadata table with columns +key+ and +value+
def create_table
unless table_exists?
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 6711ee9bf4..c2a083bf3b 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -156,7 +156,6 @@ module ActiveRecord
end
private
-
# We need to apply this decorator here, rather than on module inclusion. The closure
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
# sub class being decorated. As such, changes to `lock_optimistically`, or
diff --git a/activerecord/lib/active_record/middleware/database_selector.rb b/activerecord/lib/active_record/middleware/database_selector.rb
index 93a1a39c3e..7374107048 100644
--- a/activerecord/lib/active_record/middleware/database_selector.rb
+++ b/activerecord/lib/active_record/middleware/database_selector.rb
@@ -55,7 +55,6 @@ module ActiveRecord
end
private
-
def select_database(request, &blk)
context = context_klass.call(request)
resolver = resolver_klass.call(context, options)
diff --git a/activerecord/lib/active_record/middleware/database_selector/resolver.rb b/activerecord/lib/active_record/middleware/database_selector/resolver.rb
index 80b8cd7cae..3eb1039c50 100644
--- a/activerecord/lib/active_record/middleware/database_selector/resolver.rb
+++ b/activerecord/lib/active_record/middleware/database_selector/resolver.rb
@@ -44,10 +44,9 @@ module ActiveRecord
end
private
-
def read_from_primary(&blk)
- ActiveRecord::Base.connection.while_preventing_writes do
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
instrumenter.instrument("database_selector.active_record.read_from_primary") do
yield
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index f20edbeb93..7edfec9903 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -494,9 +494,9 @@ module ActiveRecord
# This migration will create the horses table for you on the way up, and
# automatically figure out how to drop the table on the way down.
#
- # Some commands like +remove_column+ cannot be reversed. If you care to
- # define how to move up and down in these cases, you should define the +up+
- # and +down+ methods as before.
+ # Some commands cannot be reversed. If you care to define how to move up
+ # and down in these cases, you should define the +up+ and +down+ methods
+ # as before.
#
# If a command cannot be reversed, an
# <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
@@ -568,7 +568,6 @@ module ActiveRecord
end
private
-
def connection
ActiveRecord::Base.connection
end
@@ -885,13 +884,14 @@ module ActiveRecord
def copy(destination, sources, options = {})
copied = []
+ schema_migration = options[:schema_migration] || ActiveRecord::SchemaMigration
FileUtils.mkdir_p(destination) unless File.exist?(destination)
- destination_migrations = ActiveRecord::MigrationContext.new(destination).migrations
+ destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration).migrations
last = destination_migrations.last
sources.each do |scope, path|
- source_migrations = ActiveRecord::MigrationContext.new(path).migrations
+ source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration).migrations
source_migrations.each do |migration|
source = File.binread(migration.filename)
@@ -992,7 +992,6 @@ module ActiveRecord
delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
private
-
def migration
@migration ||= load_migration
end
@@ -1014,10 +1013,11 @@ module ActiveRecord
end
class MigrationContext #:nodoc:
- attr_reader :migrations_paths
+ attr_reader :migrations_paths, :schema_migration
- def initialize(migrations_paths)
+ def initialize(migrations_paths, schema_migration)
@migrations_paths = migrations_paths
+ @schema_migration = schema_migration
end
def migrate(target_version = nil, &block)
@@ -1048,7 +1048,7 @@ module ActiveRecord
migrations
end
- Migrator.new(:up, selected_migrations, target_version).migrate
+ Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
end
def down(target_version = nil)
@@ -1058,20 +1058,20 @@ module ActiveRecord
migrations
end
- Migrator.new(:down, selected_migrations, target_version).migrate
+ Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
end
def run(direction, target_version)
- Migrator.new(direction, migrations, target_version).run
+ Migrator.new(direction, migrations, schema_migration, target_version).run
end
def open
- Migrator.new(:up, migrations, nil)
+ Migrator.new(:up, migrations, schema_migration)
end
def get_all_versions
- if SchemaMigration.table_exists?
- SchemaMigration.all_versions.map(&:to_i)
+ if schema_migration.table_exists?
+ schema_migration.all_versions.map(&:to_i)
else
[]
end
@@ -1108,12 +1108,12 @@ module ActiveRecord
end
def migrations_status
- db_list = ActiveRecord::SchemaMigration.normalized_versions
+ db_list = schema_migration.normalized_versions
file_list = migration_files.map do |file|
version, name, scope = parse_migration_filename(file)
raise IllegalMigrationNameError.new(file) unless version
- version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
+ version = schema_migration.normalize_migration_number(version)
status = db_list.delete(version) ? "up" : "down"
[status, version, (name + scope).humanize]
end.compact
@@ -1153,7 +1153,7 @@ module ActiveRecord
end
def move(direction, steps)
- migrator = Migrator.new(direction, migrations)
+ migrator = Migrator.new(direction, migrations, schema_migration)
if current_version != 0 && !migrator.current_migration
raise UnknownMigrationVersionError.new(current_version)
@@ -1172,27 +1172,28 @@ module ActiveRecord
end
end
- class Migrator #:nodoc:
+ class Migrator # :nodoc:
class << self
attr_accessor :migrations_paths
# For cases where a table doesn't exist like loading from schema cache
def current_version
- MigrationContext.new(migrations_paths).current_version
+ MigrationContext.new(migrations_paths, SchemaMigration).current_version
end
end
self.migrations_paths = ["db/migrate"]
- def initialize(direction, migrations, target_version = nil)
+ def initialize(direction, migrations, schema_migration, target_version = nil)
@direction = direction
@target_version = target_version
@migrated_versions = nil
@migrations = migrations
+ @schema_migration = schema_migration
validate(@migrations)
- ActiveRecord::SchemaMigration.create_table
+ @schema_migration.create_table
ActiveRecord::InternalMetadata.create_table
end
@@ -1246,11 +1247,10 @@ module ActiveRecord
end
def load_migrated
- @migrated_versions = Set.new(Base.connection.migration_context.get_all_versions)
+ @migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
end
private
-
# Used for running a specific migration.
def run_without_lock
migration = migrations.detect { |m| m.version == @target_version }
@@ -1330,10 +1330,10 @@ module ActiveRecord
def record_version_state_after_migrating(version)
if down?
migrated.delete(version)
- ActiveRecord::SchemaMigration.delete_by(version: version.to_s)
+ @schema_migration.delete_by(version: version.to_s)
else
migrated << version
- ActiveRecord::SchemaMigration.create!(version: version.to_s)
+ @schema_migration.create!(version: version.to_s)
end
end
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index efed4b0e26..67172ef395 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -118,7 +118,6 @@ module ActiveRecord
end
private
-
module StraightReversions # :nodoc:
private
{
diff --git a/activerecord/lib/active_record/migration/join_table.rb b/activerecord/lib/active_record/migration/join_table.rb
index 9abb289bb0..45169617c1 100644
--- a/activerecord/lib/active_record/migration/join_table.rb
+++ b/activerecord/lib/active_record/migration/join_table.rb
@@ -4,7 +4,6 @@ module ActiveRecord
class Migration
module JoinTable #:nodoc:
private
-
def find_join_table_name(table_1, table_2, options = {})
options.delete(:table_name) || join_table_name(table_1, table_2)
end
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 55fc58e339..18f19af6be 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -456,13 +456,11 @@ module ActiveRecord
end
protected
-
def initialize_load_schema_monitor
@load_schema_monitor = Monitor.new
end
private
-
def inherited(child_class)
super
child_class.initialize_load_schema_monitor
@@ -484,6 +482,9 @@ module ActiveRecord
end
def load_schema!
+ unless table_name
+ raise ActiveRecord::TableNotSpecified, "#{self} has no table configured. Set one with #{self}.table_name="
+ end
@columns_hash = connection.schema_cache.columns_hash(table_name).except(*ignored_columns)
@columns_hash.each do |name, column|
define_attribute(
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 8b9098df6c..ab107742ed 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -2,7 +2,6 @@
require "active_support/core_ext/hash/except"
require "active_support/core_ext/module/redefine_method"
-require "active_support/core_ext/object/try"
require "active_support/core_ext/hash/indifferent_access"
module ActiveRecord
@@ -354,7 +353,6 @@ module ActiveRecord
end
private
-
# Generates a writer method for this association. Serves as a point for
# accessing the objects in the association. For example, this method
# could generate the following:
@@ -386,7 +384,6 @@ module ActiveRecord
end
private
-
# Attribute hash keys that should not be assigned as normal attributes.
# These hash keys are nested attributes implementation details.
UNASSIGNABLE_KEYS = %w( id _destroy )
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
index cf0de0fdeb..bee5b5f24a 100644
--- a/activerecord/lib/active_record/null_relation.rb
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -60,7 +60,6 @@ module ActiveRecord
end
private
-
def exec_queries
@records = [].freeze
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index a58c5fd48a..323b01ab2d 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -865,7 +865,6 @@ module ActiveRecord
end
private
-
# A hook to be overridden by association modules.
def destroy_associations
end
@@ -939,7 +938,7 @@ module ActiveRecord
end
def verify_readonly_attribute(name)
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
end
def _raise_record_not_destroyed
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index a1d7c893bf..d5375390c7 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -134,7 +134,6 @@ end_error
cache = YAML.load(File.read(filename))
if cache.version == current_version
- connection.schema_cache = cache
connection_pool.schema_cache = cache.dup
else
warn "Ignoring db/schema_cache.yml because it has expired. The current schema version is #{current_version}, but the one in the cache is #{cache.version}."
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index e0bc5180c0..4d9acc911b 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -2,6 +2,8 @@
require "active_record"
+databases = ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
+
db_namespace = namespace :db do
desc "Set the environment value for the database"
task "environment:set" => :load_config do
@@ -23,7 +25,7 @@ db_namespace = namespace :db do
ActiveRecord::Tasks::DatabaseTasks.create_all
end
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
desc "Create #{spec_name} database for current environment"
task spec_name => :load_config do
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
@@ -42,7 +44,7 @@ db_namespace = namespace :db do
ActiveRecord::Tasks::DatabaseTasks.drop_all
end
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
desc "Drop #{spec_name} database for current environment"
task spec_name => [:load_config, :check_protected_environments] do
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
@@ -101,7 +103,7 @@ db_namespace = namespace :db do
end
namespace :migrate do
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
desc "Migrate #{spec_name} database for current environment"
task spec_name => :load_config do
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
@@ -110,7 +112,7 @@ db_namespace = namespace :db do
end
end
- # desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
+ desc "Rolls back the database one migration and re-migrates up (options: STEP=x, VERSION=x)."
task redo: :load_config do
raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
@@ -126,7 +128,7 @@ db_namespace = namespace :db do
# desc 'Resets your database using your migrations for the current environment'
task reset: ["db:drop", "db:create", "db:migrate"]
- # desc 'Runs the "up" for a given migration VERSION.'
+ desc 'Runs the "up" for a given migration VERSION.'
task up: :load_config do
ActiveRecord::Tasks::DatabaseTasks.raise_for_multi_db(command: "db:migrate:up")
@@ -142,7 +144,7 @@ db_namespace = namespace :db do
end
namespace :up do
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
task spec_name => :load_config do
raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
@@ -160,7 +162,7 @@ db_namespace = namespace :db do
end
end
- # desc 'Runs the "down" for a given migration VERSION.'
+ desc 'Runs the "down" for a given migration VERSION.'
task down: :load_config do
ActiveRecord::Tasks::DatabaseTasks.raise_for_multi_db(command: "db:migrate:down")
@@ -176,7 +178,7 @@ db_namespace = namespace :db do
end
namespace :down do
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
task spec_name => :load_config do
raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
@@ -203,7 +205,7 @@ db_namespace = namespace :db do
end
namespace :status do
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
desc "Display status of migrations for #{spec_name} database"
task spec_name => :load_config do
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
@@ -228,7 +230,7 @@ db_namespace = namespace :db do
db_namespace["_dump"].invoke
end
- # desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
+ desc "Drops and recreates the database from db/schema.rb for the current environment and loads the seeds."
task reset: [ "db:drop", "db:setup" ]
# desc "Retrieves the charset for the current environment's database"
@@ -250,7 +252,11 @@ db_namespace = namespace :db do
# desc "Raises an error if there are pending migrations"
task abort_if_pending_migrations: :load_config do
- pending_migrations = ActiveRecord::Base.connection.migration_context.open.pending_migrations
+ pending_migrations = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).flat_map do |db_config|
+ ActiveRecord::Base.establish_connection(db_config.config)
+
+ ActiveRecord::Base.connection.migration_context.open.pending_migrations
+ end
if pending_migrations.any?
puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}"
@@ -261,17 +267,57 @@ db_namespace = namespace :db do
end
end
+ namespace :abort_if_pending_migrations do
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
+ # desc "Raises an error if there are pending migrations for #{spec_name} database"
+ task spec_name => :load_config do
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
+ ActiveRecord::Base.establish_connection(db_config.config)
+
+ pending_migrations = ActiveRecord::Base.connection.migration_context.open.pending_migrations
+
+ if pending_migrations.any?
+ puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}"
+ pending_migrations.each do |pending_migration|
+ puts " %4d %s" % [pending_migration.version, pending_migration.name]
+ end
+ abort %{Run `rails db:migrate:#{spec_name}` to update your database then try again.}
+ end
+ end
+ end
+ end
+
desc "Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first)"
task setup: ["db:schema:load_if_ruby", "db:structure:load_if_sql", :seed]
desc "Runs setup if database does not exist, or runs migrations if it does"
task prepare: :load_config do
+ seed = false
+
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
ActiveRecord::Base.establish_connection(db_config.config)
- db_namespace["migrate"].invoke
+
+ # Skipped when no database
+ ActiveRecord::Tasks::DatabaseTasks.migrate
+ if ActiveRecord::Base.dump_schema_after_migration
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, ActiveRecord::Base.schema_format, db_config.spec_name)
+ end
+
rescue ActiveRecord::NoDatabaseError
- db_namespace["setup"].invoke
+ ActiveRecord::Tasks::DatabaseTasks.create_current(db_config.env_name, db_config.spec_name)
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(
+ db_config.config,
+ ActiveRecord::Base.schema_format,
+ nil,
+ db_config.env_name,
+ db_config.spec_name
+ )
+
+ seed = true
end
+
+ ActiveRecord::Base.establish_connection
+ ActiveRecord::Tasks::DatabaseTasks.load_seed if seed
end
desc "Loads the seed data from db/seeds.rb"
@@ -336,13 +382,9 @@ db_namespace = namespace :db do
namespace :schema do
desc "Creates a db/schema.rb file that is portable against any DB supported by Active Record"
task dump: :load_config do
- require "active_record/schema_dumper"
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
- filename = ActiveRecord::Tasks::DatabaseTasks.dump_filename(db_config.spec_name, :ruby)
- File.open(filename, "w:utf-8") do |file|
- ActiveRecord::Base.establish_connection(db_config.config)
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
- end
+ ActiveRecord::Base.establish_connection(db_config.config)
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, :ruby, db_config.spec_name)
end
db_namespace["schema:dump"].reenable
@@ -385,14 +427,7 @@ db_namespace = namespace :db do
task dump: :load_config do
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
ActiveRecord::Base.establish_connection(db_config.config)
- filename = ActiveRecord::Tasks::DatabaseTasks.dump_filename(db_config.spec_name, :sql)
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(db_config.config, filename)
- if ActiveRecord::SchemaMigration.table_exists?
- File.open(filename, "a") do |f|
- f.puts ActiveRecord::Base.connection.dump_schema_information
- f.print "\n"
- end
- end
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, :sql, db_config.spec_name)
end
db_namespace["structure:dump"].reenable
diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb
index 7bc26993d5..c851ed52c3 100644
--- a/activerecord/lib/active_record/readonly_attributes.rb
+++ b/activerecord/lib/active_record/readonly_attributes.rb
@@ -19,6 +19,10 @@ module ActiveRecord
def readonly_attributes
_attr_readonly
end
+
+ def readonly_attribute?(name) # :nodoc:
+ _attr_readonly.include?(name)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index eefda2b8f4..cbfa60d4d9 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -590,7 +590,6 @@ module ActiveRecord
end
private
-
def calculate_constructable(macro, options)
true
end
@@ -704,7 +703,6 @@ module ActiveRecord
end
private
-
def calculate_constructable(macro, options)
!options[:through]
end
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 9c579843b1..30b8edd0bd 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -258,7 +258,6 @@ module ActiveRecord
end
private
-
def apply_limits(relation, start, finish)
relation = apply_start_limit(relation, start) if start
relation = apply_finish_limit(relation, finish) if finish
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 0be9ba7d7b..0a14a33c1d 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -340,7 +340,7 @@ module ActiveRecord
}
relation = except(:group).distinct!(false)
- relation.group_values = group_aliases
+ relation.group_values = group_fields
relation.select_values = select_values
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index d59331053e..2f61c05eca 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -99,7 +99,6 @@ module ActiveRecord
end
private
-
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
@klass.generate_relation_method(method)
@@ -116,7 +115,6 @@ module ActiveRecord
end
private
-
def relation_class_for(klass)
klass.relation_delegate_class(self)
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 9c7ac80447..1dbf4808fd 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -346,7 +346,6 @@ module ActiveRecord
end
private
-
def offset_index
offset_value || 0
end
@@ -355,7 +354,7 @@ module ActiveRecord
conditions = sanitize_forbidden_attributes(conditions)
if distinct_value && offset_value
- relation = limit(1)
+ relation = except(:order).limit!(1)
else
relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
end
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index 84fe424ef0..e1735c0522 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -89,7 +89,6 @@ module ActiveRecord
end
private
-
def merge_preloads
return if other.preload_values.empty? && other.includes_values.empty?
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 50ff733dc7..6957ba052b 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -138,7 +138,7 @@ module ActiveRecord
end
def includes!(*args) # :nodoc:
- args.reject!(&:blank?)
+ args.compact_blank!
args.flatten!
self.includes_values |= args
@@ -265,7 +265,7 @@ module ActiveRecord
end
def _select!(*fields) # :nodoc:
- fields.reject!(&:blank?)
+ fields.compact_blank!
fields.flatten!
self.select_values += fields
self
@@ -952,7 +952,7 @@ module ActiveRecord
def optimizer_hints!(*args) # :nodoc:
args.flatten!
- self.optimizer_hints_values += args
+ self.optimizer_hints_values |= args
self
end
@@ -965,7 +965,7 @@ module ActiveRecord
def reverse_order! # :nodoc:
orders = order_values.uniq
- orders.reject!(&:blank?)
+ orders.compact_blank!
self.order_values = reverse_sql_order(orders)
self
end
@@ -1053,7 +1053,7 @@ module ActiveRecord
)
arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
end
- arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
+ arel.group(*arel_columns(group_values.uniq.compact_blank)) unless group_values.empty?
build_order(arel)
@@ -1129,7 +1129,7 @@ module ActiveRecord
association_joins = buckets[:association_join]
stashed_joins = buckets[:stashed_join]
join_nodes = buckets[:join_node].tap(&:uniq!)
- string_joins = buckets[:string_join].delete_if(&:blank?).map!(&:strip).tap(&:uniq!)
+ string_joins = buckets[:string_join].compact_blank!.map!(&:strip).tap(&:uniq!)
string_joins.map! { |join| table.create_string_join(Arel.sql(join)) }
@@ -1159,8 +1159,9 @@ module ActiveRecord
columns.flat_map do |field|
case field
when Symbol
- field = field.to_s
- arel_column(field, &connection.method(:quote_table_name))
+ arel_column(field.to_s) do |attr_name|
+ connection.quote_table_name(attr_name)
+ end
when String
arel_column(field, &:itself)
when Proc
@@ -1226,7 +1227,7 @@ module ActiveRecord
def build_order(arel)
orders = order_values.uniq
- orders.reject!(&:blank?)
+ orders.compact_blank!
arel.order(*orders) unless orders.empty?
end
@@ -1247,6 +1248,7 @@ module ActiveRecord
end
def preprocess_order_args(order_args)
+ order_args.reject!(&:blank?)
order_args.map! do |arg|
klass.sanitize_sql_for_order(arg)
end
@@ -1254,7 +1256,7 @@ module ActiveRecord
@klass.disallow_raw_sql!(
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
- permit: AttributeMethods::ClassMethods::COLUMN_NAME_WITH_ORDER
+ permit: connection.column_name_with_order_matcher
)
validate_order_args(order_args)
@@ -1267,20 +1269,14 @@ module ActiveRecord
order_args.map! do |arg|
case arg
when Symbol
- arg = arg.to_s
- arel_column(arg) {
- Arel.sql(connection.quote_table_name(arg))
- }.asc
+ order_column(arg.to_s).asc
when Hash
arg.map { |field, dir|
case field
when Arel::Nodes::SqlLiteral
field.send(dir.downcase)
else
- field = field.to_s
- arel_column(field) {
- Arel.sql(connection.quote_table_name(field))
- }.send(dir.downcase)
+ order_column(field.to_s).send(dir.downcase)
end
}
else
@@ -1289,6 +1285,16 @@ module ActiveRecord
end.flatten!
end
+ def order_column(field)
+ arel_column(field) do |attr_name|
+ if attr_name == "count" && !group_values.empty?
+ arel_attribute(attr_name)
+ else
+ Arel.sql(connection.quote_table_name(attr_name))
+ end
+ end
+ end
+
# Checks to make sure that the arguments are not blank. Note that if some
# blank-like object were initially passed into the query method, then this
# method will not raise an error.
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index efc4b447aa..3f6dd50139 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -67,7 +67,6 @@ module ActiveRecord
end
private
-
def relation_with(values)
result = Relation.create(klass, values: values)
result.extend(*extending_values) if extending_values.any?
diff --git a/activerecord/lib/active_record/relation/where_clause.rb b/activerecord/lib/active_record/relation/where_clause.rb
index b91b135867..8fae380b0a 100644
--- a/activerecord/lib/active_record/relation/where_clause.rb
+++ b/activerecord/lib/active_record/relation/where_clause.rb
@@ -87,7 +87,6 @@ module ActiveRecord
end
protected
-
attr_reader :predicates
def referenced_columns
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index da6d10b6ec..3b615f29a3 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -132,7 +132,6 @@ module ActiveRecord
end
private
-
def column_type(name, type_overrides = {})
type_overrides.fetch(name) do
column_types.fetch(name, Type.default_value)
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 750766714d..b16cbb0f84 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -61,8 +61,9 @@ module ActiveRecord
# # => "id ASC"
def sanitize_sql_for_order(condition)
if condition.is_a?(Array) && condition.first.to_s.include?("?")
- disallow_raw_sql!([condition.first],
- permit: AttributeMethods::ClassMethods::COLUMN_NAME_WITH_ORDER
+ disallow_raw_sql!(
+ [condition.first],
+ permit: connection.column_name_with_order_matcher
)
# Ensure we aren't dealing with a subclass of String that might
@@ -133,6 +134,33 @@ module ActiveRecord
end
end
+ def disallow_raw_sql!(args, permit: connection.column_name_matcher) # :nodoc:
+ unexpected = nil
+ args.each do |arg|
+ next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s)
+ (unexpected ||= []) << arg
+ end
+
+ return unless unexpected
+
+ if allow_unsafe_raw_sql == :deprecated
+ ActiveSupport::Deprecation.warn(
+ "Dangerous query method (method whose arguments are used as raw " \
+ "SQL) called with non-attribute argument(s): " \
+ "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
+ "arguments will be disallowed in Rails 6.1. This method should " \
+ "not be called with user-provided values, such as request " \
+ "parameters or model attributes. Known-safe values can be passed " \
+ "by wrapping them in Arel.sql()."
+ )
+ else
+ raise(ActiveRecord::UnknownAttributeReference,
+ "Query method called with non-attribute argument(s): " +
+ unexpected.map(&:inspect).join(", ")
+ )
+ end
+ end
+
private
def replace_bind_variables(statement, values)
raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb
index 76bf53387d..aba25fb375 100644
--- a/activerecord/lib/active_record/schema.rb
+++ b/activerecord/lib/active_record/schema.rb
@@ -50,7 +50,7 @@ module ActiveRecord
instance_eval(&block)
if info[:version].present?
- ActiveRecord::SchemaMigration.create_table
+ connection.schema_migration.create_table
connection.assume_migrated_upto_version(info[:version])
end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 2f7cc07221..f4b1f536b3 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -146,7 +146,11 @@ HEADER
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
next if column.name == pk
type, colspec = column_spec(column)
- tbl.print " t.#{type} #{column.name.inspect}"
+ if type.is_a?(Symbol)
+ tbl.print " t.#{type} #{column.name.inspect}"
+ else
+ tbl.print " t.column #{column.name.inspect}, #{type.inspect}"
+ end
tbl.print ", #{format_colspec(colspec)}" if colspec.present?
tbl.puts
end
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
index 74547de862..dec7fee986 100644
--- a/activerecord/lib/active_record/schema_migration.rb
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -22,10 +22,6 @@ module ActiveRecord
"#{table_name_prefix}#{schema_migrations_table_name}#{table_name_suffix}"
end
- def table_exists?
- connection.table_exists?(table_name)
- end
-
def create_table
unless table_exists?
version_options = connection.internal_string_options_for_primary_key
diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb
index 35e9dcbffc..62c7988bd8 100644
--- a/activerecord/lib/active_record/scoping.rb
+++ b/activerecord/lib/active_record/scoping.rb
@@ -95,7 +95,6 @@ module ActiveRecord
end
private
-
def raise_invalid_scope_type!(scope_type)
if !VALID_SCOPE_TYPES.include?(scope_type)
raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index 87bcfd5181..151eef362b 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -44,7 +44,6 @@ module ActiveRecord
end
private
-
# Use this macro in your model to set a default scope for all operations on
# the model.
#
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index cd9801b7a0..7baef99e83 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -204,7 +204,6 @@ module ActiveRecord
end
private
-
def valid_scope_name?(name)
if respond_to?(name, true) && logger
logger.warn "Creating scope :#{name}. " \
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index c79ed8db60..5d1ce19829 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -141,10 +141,21 @@ module ActiveRecord
end
end
- def for_each
+ def setup_initial_database_yaml
+ return {} unless defined?(Rails)
+
+ begin
+ Rails.application.config.load_database_yaml
+ rescue
+ $stderr.puts "Rails couldn't infer whether you are using multiple databases from your database.yml and can't generate the tasks for the non-primary databases. If you'd like to use this feature, please simplify your ERB."
+
+ {}
+ end
+ end
+
+ def for_each(databases)
return {} unless defined?(Rails)
- databases = Rails.application.config.load_database_yaml
database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
# if this is a single database application we don't want tasks for each primary database
@@ -169,8 +180,8 @@ module ActiveRecord
end
end
- def create_current(environment = env)
- each_current_configuration(environment) { |configuration|
+ def create_current(environment = env, spec_name = nil)
+ each_current_configuration(environment, spec_name) { |configuration|
create configuration
}
ActiveRecord::Base.establish_connection(environment.to_sym)
@@ -200,9 +211,10 @@ module ActiveRecord
def truncate_tables(configuration)
ActiveRecord::Base.connected_to(database: { truncation: configuration }) do
- table_names = ActiveRecord::Base.connection.tables
+ conn = ActiveRecord::Base.connection
+ table_names = conn.tables
table_names -= [
- SchemaMigration.table_name,
+ conn.schema_migration.table_name,
InternalMetadata.table_name
]
@@ -233,7 +245,7 @@ module ActiveRecord
end
def migrate_status
- unless ActiveRecord::SchemaMigration.table_exists?
+ unless ActiveRecord::Base.connection.schema_migration.table_exists?
Kernel.abort "Schema migrations table does not exist yet."
end
@@ -325,6 +337,27 @@ module ActiveRecord
Migration.verbose = verbose_was
end
+ def dump_schema(configuration, format = ActiveRecord::Base.schema_format, spec_name = "primary") # :nodoc:
+ require "active_record/schema_dumper"
+ filename = dump_filename(spec_name, format)
+ connection = ActiveRecord::Base.connection
+
+ case format
+ when :ruby
+ File.open(filename, "w:utf-8") do |file|
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
+ end
+ when :sql
+ structure_dump(configuration, filename)
+ if connection.schema_migration.table_exists?
+ File.open(filename, "a") do |f|
+ f.puts connection.dump_schema_information
+ f.print "\n"
+ end
+ end
+ end
+ end
+
def schema_file(format = ActiveRecord::Base.schema_format)
File.join(db_dir, schema_file_type(format))
end
@@ -406,12 +439,14 @@ module ActiveRecord
task.is_a?(String) ? task.constantize : task
end
- def each_current_configuration(environment)
+ def each_current_configuration(environment, spec_name = nil)
environments = [environment]
environments << "test" if environment == "development"
environments.each do |env|
ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
+ next if spec_name && spec_name != db_config.spec_name
+
yield db_config.config, db_config.spec_name, env
end
end
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index 1c1b29b5e1..a7e04007a9 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -3,6 +3,8 @@
module ActiveRecord
module Tasks # :nodoc:
class MySQLDatabaseTasks # :nodoc:
+ ER_DB_CREATE_EXISTS = 1007
+
delegate :connection, :establish_connection, to: ActiveRecord::Base
def initialize(configuration)
@@ -14,7 +16,7 @@ module ActiveRecord
connection.create_database configuration["database"], creation_options
establish_connection configuration
rescue ActiveRecord::StatementInvalid => error
- if error.message.include?("database exists")
+ if error.cause.error_number == ER_DB_CREATE_EXISTS
raise DatabaseAlreadyExists
else
raise
@@ -67,7 +69,6 @@ module ActiveRecord
end
private
-
attr_reader :configuration
def configuration_without_database
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index 8acb11f75f..626ffdfdf9 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -89,7 +89,6 @@ module ActiveRecord
end
private
-
attr_reader :configuration
def encoding
diff --git a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
index a82cea80ca..f67a3498b6 100644
--- a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
@@ -59,7 +59,6 @@ module ActiveRecord
end
private
-
attr_reader :configuration, :root
def run_cmd(cmd, args, out)
diff --git a/activerecord/lib/active_record/test_fixtures.rb b/activerecord/lib/active_record/test_fixtures.rb
index 8c60d71669..1d6fef1eb9 100644
--- a/activerecord/lib/active_record/test_fixtures.rb
+++ b/activerecord/lib/active_record/test_fixtures.rb
@@ -179,7 +179,6 @@ module ActiveRecord
end
private
-
# Shares the writing connection pool with connections on
# other handlers.
#
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 04a1c03474..c883d368b5 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -59,19 +59,26 @@ module ActiveRecord
attribute_names.index_with(time || current_time_from_proper_timezone)
end
- private
- def timestamp_attributes_for_create_in_model
- timestamp_attributes_for_create.select { |c| column_names.include?(c) }
- end
+ def timestamp_attributes_for_create_in_model
+ @timestamp_attributes_for_create_in_model ||=
+ (timestamp_attributes_for_create & column_names).freeze
+ end
- def timestamp_attributes_for_update_in_model
- timestamp_attributes_for_update.select { |c| column_names.include?(c) }
- end
+ def timestamp_attributes_for_update_in_model
+ @timestamp_attributes_for_update_in_model ||=
+ (timestamp_attributes_for_update & column_names).freeze
+ end
- def all_timestamp_attributes_in_model
- timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
- end
+ def all_timestamp_attributes_in_model
+ @all_timestamp_attributes_in_model ||=
+ (timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model).freeze
+ end
+
+ def current_time_from_proper_timezone
+ default_timezone == :utc ? Time.now.utc : Time.now
+ end
+ private
def timestamp_attributes_for_create
["created_at", "created_on"]
end
@@ -80,13 +87,15 @@ module ActiveRecord
["updated_at", "updated_on"]
end
- def current_time_from_proper_timezone
- default_timezone == :utc ? Time.now.utc : Time.now
+ def reload_schema_from_cache
+ @timestamp_attributes_for_create_in_model = nil
+ @timestamp_attributes_for_update_in_model = nil
+ @all_timestamp_attributes_in_model = nil
+ super
end
end
private
-
def _create_record
if record_timestamps
current_time = current_time_from_proper_timezone
@@ -124,19 +133,19 @@ module ActiveRecord
end
def timestamp_attributes_for_create_in_model
- self.class.send(:timestamp_attributes_for_create_in_model)
+ self.class.timestamp_attributes_for_create_in_model
end
def timestamp_attributes_for_update_in_model
- self.class.send(:timestamp_attributes_for_update_in_model)
+ self.class.timestamp_attributes_for_update_in_model
end
def all_timestamp_attributes_in_model
- self.class.send(:all_timestamp_attributes_in_model)
+ self.class.all_timestamp_attributes_in_model
end
def current_time_from_proper_timezone
- self.class.send(:current_time_from_proper_timezone)
+ self.class.current_time_from_proper_timezone
end
def max_updated_column_timestamp
diff --git a/activerecord/lib/active_record/touch_later.rb b/activerecord/lib/active_record/touch_later.rb
index bc63c8d987..3981bd46ad 100644
--- a/activerecord/lib/active_record/touch_later.rb
+++ b/activerecord/lib/active_record/touch_later.rb
@@ -36,7 +36,6 @@ module ActiveRecord
end
private
-
def surreptitiously_touch(attrs)
attrs.each { |attr| write_attribute attr, @_touch_time }
clear_attribute_changes attrs
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index cbb970ac98..5113e08e8e 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -282,7 +282,6 @@ module ActiveRecord
end
private
-
def set_options_for_callbacks!(args, enforced_options = {})
options = args.extract_options!.merge!(enforced_options)
args << options
diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb
index 03d00006b7..4c1ef1a7e4 100644
--- a/activerecord/lib/active_record/type.rb
+++ b/activerecord/lib/active_record/type.rb
@@ -47,7 +47,6 @@ module ActiveRecord
end
private
-
def current_adapter_name
ActiveRecord::Base.connection.adapter_name.downcase.to_sym
end
diff --git a/activerecord/lib/active_record/type/adapter_specific_registry.rb b/activerecord/lib/active_record/type/adapter_specific_registry.rb
index b300fdfa05..c8c16635b1 100644
--- a/activerecord/lib/active_record/type/adapter_specific_registry.rb
+++ b/activerecord/lib/active_record/type/adapter_specific_registry.rb
@@ -11,7 +11,6 @@ module ActiveRecord
end
private
-
def registration_klass
Registration
end
@@ -53,7 +52,6 @@ module ActiveRecord
end
protected
-
attr_reader :name, :block, :adapter, :override
def priority
@@ -72,7 +70,6 @@ module ActiveRecord
end
private
-
def matches_adapter?(adapter: nil, **)
(self.adapter.nil? || adapter == self.adapter)
end
diff --git a/activerecord/lib/active_record/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/type/hash_lookup_type_map.rb
index db9853fbcc..b260464df5 100644
--- a/activerecord/lib/active_record/type/hash_lookup_type_map.rb
+++ b/activerecord/lib/active_record/type/hash_lookup_type_map.rb
@@ -16,7 +16,6 @@ module ActiveRecord
end
private
-
def perform_fetch(type, *args, &block)
@mapping.fetch(type, block).call(type, *args)
end
diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb
index 0a2f6cb9fb..a34b2fe702 100644
--- a/activerecord/lib/active_record/type/serialized.rb
+++ b/activerecord/lib/active_record/type/serialized.rb
@@ -56,7 +56,6 @@ module ActiveRecord
end
private
-
def default_value?(value)
value == coder.load(nil)
end
diff --git a/activerecord/lib/active_record/type/type_map.rb b/activerecord/lib/active_record/type/type_map.rb
index fc40b460f0..58f25ba075 100644
--- a/activerecord/lib/active_record/type/type_map.rb
+++ b/activerecord/lib/active_record/type/type_map.rb
@@ -45,7 +45,6 @@ module ActiveRecord
end
private
-
def perform_fetch(lookup_key, *args)
matching_pair = @mapping.reverse_each.detect do |key, _|
key === lookup_key
diff --git a/activerecord/lib/active_record/type/unsigned_integer.rb b/activerecord/lib/active_record/type/unsigned_integer.rb
index 4619528f81..535369e630 100644
--- a/activerecord/lib/active_record/type/unsigned_integer.rb
+++ b/activerecord/lib/active_record/type/unsigned_integer.rb
@@ -4,7 +4,6 @@ module ActiveRecord
module Type
class UnsignedInteger < ActiveModel::Type::Integer # :nodoc:
private
-
def max_value
super * 2
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index ca27a3f0ab..23e8d53168 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -71,7 +71,6 @@ module ActiveRecord
alias_method :validate, :valid?
private
-
def default_validation_context
new_record? ? :create : :update
end
diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb
index 3538aeec22..dc89df4be7 100644
--- a/activerecord/lib/active_record/validations/associated.rb
+++ b/activerecord/lib/active_record/validations/associated.rb
@@ -10,7 +10,6 @@ module ActiveRecord
end
private
-
def valid_object?(record)
(record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?
end
diff --git a/activerecord/lib/arel.rb b/activerecord/lib/arel.rb
index 361cd915cc..0fc07e1ede 100644
--- a/activerecord/lib/arel.rb
+++ b/activerecord/lib/arel.rb
@@ -12,7 +12,7 @@ require "arel/math"
require "arel/alias_predication"
require "arel/order_predications"
require "arel/table"
-require "arel/attributes"
+require "arel/attributes/attribute"
require "arel/visitors"
require "arel/collectors/sql_string"
diff --git a/activerecord/lib/arel/attributes.rb b/activerecord/lib/arel/attributes.rb
deleted file mode 100644
index 35d586c948..0000000000
--- a/activerecord/lib/arel/attributes.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-require "arel/attributes/attribute"
-
-module Arel # :nodoc: all
- module Attributes
- ###
- # Factory method to wrap a raw database +column+ to an Arel Attribute.
- def self.for(column)
- case column.type
- when :string, :text, :binary then String
- when :integer then Integer
- when :float then Float
- when :decimal then Decimal
- when :date, :datetime, :timestamp, :time then Time
- when :boolean then Boolean
- else
- Undefined
- end
- end
- end
-end
diff --git a/activerecord/lib/arel/nodes/node.rb b/activerecord/lib/arel/nodes/node.rb
index 8086102bde..0416ff58de 100644
--- a/activerecord/lib/arel/nodes/node.rb
+++ b/activerecord/lib/arel/nodes/node.rb
@@ -6,7 +6,6 @@ module Arel # :nodoc: all
# Abstract base class for all AST nodes
class Node
include Arel::FactoryMethods
- include Enumerable
###
# Factory method to create a Nodes::Not node that has the recipient of
@@ -38,13 +37,6 @@ module Arel # :nodoc: all
collector = engine.connection.visitor.accept self, collector
collector.value
end
-
- # Iterate through AST, nodes will be yielded depth-first
- def each(&block)
- return enum_for(:each) unless block_given?
-
- ::Arel::Visitors::DepthFirst.new(block).accept self
- end
end
end
end
diff --git a/activerecord/lib/arel/predications.rb b/activerecord/lib/arel/predications.rb
index 7dafde4952..895d394363 100644
--- a/activerecord/lib/arel/predications.rb
+++ b/activerecord/lib/arel/predications.rb
@@ -37,7 +37,7 @@ module Arel # :nodoc: all
def between(other)
if unboundable?(other.begin) == 1 || unboundable?(other.end) == -1
self.in([])
- elsif open_ended?(other.begin)
+ elsif other.begin.nil? || open_ended?(other.begin)
if other.end.nil? || open_ended?(other.end)
not_in([])
elsif other.exclude_end?
@@ -85,7 +85,7 @@ Passing a range to `#in` is deprecated. Call `#between`, instead.
def not_between(other)
if unboundable?(other.begin) == 1 || unboundable?(other.end) == -1
not_in([])
- elsif open_ended?(other.begin)
+ elsif other.begin.nil? || open_ended?(other.begin)
if other.end.nil? || open_ended?(other.end)
self.in([])
elsif other.exclude_end?
@@ -221,7 +221,6 @@ Passing a range to `#not_in` is deprecated. Call `#not_between`, instead.
end
private
-
def grouping_any(method_id, others, *extras)
nodes = others.map { |expr| send(method_id, expr, *extras) }
Nodes::Grouping.new nodes.inject { |memo, node|
diff --git a/activerecord/lib/arel/visitors.rb b/activerecord/lib/arel/visitors.rb
index e350f52e65..a1097f6750 100644
--- a/activerecord/lib/arel/visitors.rb
+++ b/activerecord/lib/arel/visitors.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "arel/visitors/visitor"
-require "arel/visitors/depth_first"
require "arel/visitors/to_sql"
require "arel/visitors/sqlite"
require "arel/visitors/postgresql"
diff --git a/activerecord/lib/arel/visitors/depth_first.rb b/activerecord/lib/arel/visitors/depth_first.rb
deleted file mode 100644
index d696edc507..0000000000
--- a/activerecord/lib/arel/visitors/depth_first.rb
+++ /dev/null
@@ -1,204 +0,0 @@
-# frozen_string_literal: true
-
-module Arel # :nodoc: all
- module Visitors
- class DepthFirst < Arel::Visitors::Visitor
- def initialize(block = nil)
- @block = block || Proc.new
- super()
- end
-
- private
-
- def visit(o)
- super
- @block.call o
- end
-
- def unary(o)
- visit o.expr
- end
- alias :visit_Arel_Nodes_Else :unary
- alias :visit_Arel_Nodes_Group :unary
- alias :visit_Arel_Nodes_Cube :unary
- alias :visit_Arel_Nodes_RollUp :unary
- alias :visit_Arel_Nodes_GroupingSet :unary
- alias :visit_Arel_Nodes_GroupingElement :unary
- alias :visit_Arel_Nodes_Grouping :unary
- alias :visit_Arel_Nodes_Having :unary
- alias :visit_Arel_Nodes_Lateral :unary
- alias :visit_Arel_Nodes_Limit :unary
- alias :visit_Arel_Nodes_Not :unary
- alias :visit_Arel_Nodes_Offset :unary
- alias :visit_Arel_Nodes_On :unary
- alias :visit_Arel_Nodes_Ordering :unary
- alias :visit_Arel_Nodes_Ascending :unary
- alias :visit_Arel_Nodes_Descending :unary
- alias :visit_Arel_Nodes_UnqualifiedColumn :unary
- alias :visit_Arel_Nodes_OptimizerHints :unary
- alias :visit_Arel_Nodes_ValuesList :unary
-
- def function(o)
- visit o.expressions
- visit o.alias
- visit o.distinct
- end
- alias :visit_Arel_Nodes_Avg :function
- alias :visit_Arel_Nodes_Exists :function
- alias :visit_Arel_Nodes_Max :function
- alias :visit_Arel_Nodes_Min :function
- alias :visit_Arel_Nodes_Sum :function
-
- def visit_Arel_Nodes_NamedFunction(o)
- visit o.name
- visit o.expressions
- visit o.distinct
- visit o.alias
- end
-
- def visit_Arel_Nodes_Count(o)
- visit o.expressions
- visit o.alias
- visit o.distinct
- end
-
- def visit_Arel_Nodes_Case(o)
- visit o.case
- visit o.conditions
- visit o.default
- end
-
- def nary(o)
- o.children.each { |child| visit child }
- end
- alias :visit_Arel_Nodes_And :nary
-
- def binary(o)
- visit o.left
- visit o.right
- end
- alias :visit_Arel_Nodes_As :binary
- alias :visit_Arel_Nodes_Assignment :binary
- alias :visit_Arel_Nodes_Between :binary
- alias :visit_Arel_Nodes_Concat :binary
- alias :visit_Arel_Nodes_DeleteStatement :binary
- alias :visit_Arel_Nodes_DoesNotMatch :binary
- alias :visit_Arel_Nodes_Equality :binary
- alias :visit_Arel_Nodes_FullOuterJoin :binary
- alias :visit_Arel_Nodes_GreaterThan :binary
- alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
- alias :visit_Arel_Nodes_In :binary
- alias :visit_Arel_Nodes_InfixOperation :binary
- alias :visit_Arel_Nodes_JoinSource :binary
- alias :visit_Arel_Nodes_InnerJoin :binary
- alias :visit_Arel_Nodes_LessThan :binary
- alias :visit_Arel_Nodes_LessThanOrEqual :binary
- alias :visit_Arel_Nodes_Matches :binary
- alias :visit_Arel_Nodes_NotEqual :binary
- alias :visit_Arel_Nodes_NotIn :binary
- alias :visit_Arel_Nodes_NotRegexp :binary
- alias :visit_Arel_Nodes_IsNotDistinctFrom :binary
- alias :visit_Arel_Nodes_IsDistinctFrom :binary
- alias :visit_Arel_Nodes_Or :binary
- alias :visit_Arel_Nodes_OuterJoin :binary
- alias :visit_Arel_Nodes_Regexp :binary
- alias :visit_Arel_Nodes_RightOuterJoin :binary
- alias :visit_Arel_Nodes_TableAlias :binary
- alias :visit_Arel_Nodes_When :binary
-
- def visit_Arel_Nodes_StringJoin(o)
- visit o.left
- end
-
- def visit_Arel_Attribute(o)
- visit o.relation
- visit o.name
- end
- alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
- alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
- alias :visit_Arel_Attributes_String :visit_Arel_Attribute
- alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
- alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
- alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
- alias :visit_Arel_Attributes_Decimal :visit_Arel_Attribute
-
- def visit_Arel_Table(o)
- visit o.name
- end
-
- def terminal(o)
- end
- alias :visit_ActiveSupport_Multibyte_Chars :terminal
- alias :visit_ActiveSupport_StringInquirer :terminal
- alias :visit_Arel_Nodes_Lock :terminal
- alias :visit_Arel_Nodes_Node :terminal
- alias :visit_Arel_Nodes_SqlLiteral :terminal
- alias :visit_Arel_Nodes_BindParam :terminal
- alias :visit_Arel_Nodes_Window :terminal
- alias :visit_Arel_Nodes_True :terminal
- alias :visit_Arel_Nodes_False :terminal
- alias :visit_BigDecimal :terminal
- alias :visit_Class :terminal
- alias :visit_Date :terminal
- alias :visit_DateTime :terminal
- alias :visit_FalseClass :terminal
- alias :visit_Float :terminal
- alias :visit_Integer :terminal
- alias :visit_NilClass :terminal
- alias :visit_String :terminal
- alias :visit_Symbol :terminal
- alias :visit_Time :terminal
- alias :visit_TrueClass :terminal
-
- def visit_Arel_Nodes_InsertStatement(o)
- visit o.relation
- visit o.columns
- visit o.values
- end
-
- def visit_Arel_Nodes_SelectCore(o)
- visit o.projections
- visit o.source
- visit o.wheres
- visit o.groups
- visit o.windows
- visit o.havings
- end
-
- def visit_Arel_Nodes_SelectStatement(o)
- visit o.cores
- visit o.orders
- visit o.limit
- visit o.lock
- visit o.offset
- end
-
- def visit_Arel_Nodes_UpdateStatement(o)
- visit o.relation
- visit o.values
- visit o.wheres
- visit o.orders
- visit o.limit
- end
-
- def visit_Arel_Nodes_Comment(o)
- visit o.values
- end
-
- def visit_Array(o)
- o.each { |i| visit i }
- end
- alias :visit_Set :visit_Array
-
- def visit_Hash(o)
- o.each { |k, v| visit(k); visit(v) }
- end
-
- DISPATCH = dispatch_cache
-
- def get_dispatch_cache
- DISPATCH
- end
- end
- end
-end
diff --git a/activerecord/lib/arel/visitors/dot.rb b/activerecord/lib/arel/visitors/dot.rb
index ecc386de07..c4ea07bcfe 100644
--- a/activerecord/lib/arel/visitors/dot.rb
+++ b/activerecord/lib/arel/visitors/dot.rb
@@ -31,7 +31,6 @@ module Arel # :nodoc: all
end
private
-
def visit_Arel_Nodes_Ordering(o)
visit_edge o, "expr"
end
diff --git a/activerecord/lib/arel/visitors/mssql.rb b/activerecord/lib/arel/visitors/mssql.rb
index 8475139870..92eb94f802 100644
--- a/activerecord/lib/arel/visitors/mssql.rb
+++ b/activerecord/lib/arel/visitors/mssql.rb
@@ -11,7 +11,6 @@ module Arel # :nodoc: all
end
private
-
def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
right = o.right
diff --git a/activerecord/lib/arel/visitors/oracle.rb b/activerecord/lib/arel/visitors/oracle.rb
index f96bf65ee5..aab66301ef 100644
--- a/activerecord/lib/arel/visitors/oracle.rb
+++ b/activerecord/lib/arel/visitors/oracle.rb
@@ -4,7 +4,6 @@ module Arel # :nodoc: all
module Visitors
class Oracle < Arel::Visitors::ToSql
private
-
def visit_Arel_Nodes_SelectStatement(o, collector)
o = order_hacks(o)
diff --git a/activerecord/lib/arel/visitors/oracle12.rb b/activerecord/lib/arel/visitors/oracle12.rb
index 6269bc3907..36783243b5 100644
--- a/activerecord/lib/arel/visitors/oracle12.rb
+++ b/activerecord/lib/arel/visitors/oracle12.rb
@@ -4,7 +4,6 @@ module Arel # :nodoc: all
module Visitors
class Oracle12 < Arel::Visitors::ToSql
private
-
def visit_Arel_Nodes_SelectStatement(o, collector)
# Oracle does not allow LIMIT clause with select for update
if o.limit && o.lock
diff --git a/activerecord/lib/arel/visitors/postgresql.rb b/activerecord/lib/arel/visitors/postgresql.rb
index 8296f1cdc1..d4f21ff93e 100644
--- a/activerecord/lib/arel/visitors/postgresql.rb
+++ b/activerecord/lib/arel/visitors/postgresql.rb
@@ -4,7 +4,6 @@ module Arel # :nodoc: all
module Visitors
class PostgreSQL < Arel::Visitors::ToSql
private
-
def visit_Arel_Nodes_Matches(o, collector)
op = o.case_sensitive ? " LIKE " : " ILIKE "
collector = infix_value o, collector, op
diff --git a/activerecord/lib/arel/visitors/sqlite.rb b/activerecord/lib/arel/visitors/sqlite.rb
index af6f7e856a..62ec74ad82 100644
--- a/activerecord/lib/arel/visitors/sqlite.rb
+++ b/activerecord/lib/arel/visitors/sqlite.rb
@@ -4,7 +4,6 @@ module Arel # :nodoc: all
module Visitors
class SQLite < Arel::Visitors::ToSql
private
-
# Locks are not supported in SQLite
def visit_Arel_Nodes_Lock(o, collector)
collector
diff --git a/activerecord/lib/arel/visitors/to_sql.rb b/activerecord/lib/arel/visitors/to_sql.rb
index 4740e6d94f..eff7a0d036 100644
--- a/activerecord/lib/arel/visitors/to_sql.rb
+++ b/activerecord/lib/arel/visitors/to_sql.rb
@@ -19,7 +19,6 @@ module Arel # :nodoc: all
end
private
-
def visit_Arel_Nodes_DeleteStatement(o, collector)
o = prepare_delete_statement(o)
@@ -52,10 +51,14 @@ module Arel # :nodoc: all
def visit_Arel_Nodes_InsertStatement(o, collector)
collector << "INSERT INTO "
collector = visit o.relation, collector
- if o.columns.any?
- collector << " (#{o.columns.map { |x|
- quote_column_name x.name
- }.join ', '})"
+
+ unless o.columns.empty?
+ collector << " ("
+ o.columns.each_with_index do |x, i|
+ collector << ", " unless i == 0
+ collector << quote_column_name(x.name)
+ end
+ collector << ")"
end
if o.values
@@ -97,22 +100,20 @@ module Arel # :nodoc: all
def visit_Arel_Nodes_ValuesList(o, collector)
collector << "VALUES "
- len = o.rows.length - 1
- o.rows.each_with_index { |row, i|
+ o.rows.each_with_index do |row, i|
+ collector << ", " unless i == 0
collector << "("
- row_len = row.length - 1
row.each_with_index do |value, k|
+ collector << ", " unless k == 0
case value
when Nodes::SqlLiteral, Nodes::BindParam
collector = visit(value, collector)
else
collector << quote(value).to_s
end
- collector << ", " unless k == row_len
end
collector << ")"
- collector << ", " unless i == len
- }
+ end
collector
end
@@ -128,11 +129,10 @@ module Arel # :nodoc: all
unless o.orders.empty?
collector << " ORDER BY "
- len = o.orders.length - 1
- o.orders.each_with_index { |x, i|
+ o.orders.each_with_index do |x, i|
+ collector << ", " unless i == 0
collector = visit(x, collector)
- collector << ", " unless len == i
- }
+ end
end
visit_Arel_Nodes_SelectOptions(o, collector)
@@ -506,7 +506,7 @@ module Arel # :nodoc: all
def visit_Arel_Table(o, collector)
if o.table_alias
- collector << "#{quote_table_name o.name} #{quote_table_name o.table_alias}"
+ collector << quote_table_name(o.name) << " " << quote_table_name(o.table_alias)
else
collector << quote_table_name(o.name)
end
@@ -682,20 +682,13 @@ module Arel # :nodoc: all
end
def visit_Arel_Nodes_UnqualifiedColumn(o, collector)
- collector << "#{quote_column_name o.name}"
- collector
+ collector << quote_column_name(o.name)
end
def visit_Arel_Attributes_Attribute(o, collector)
join_name = o.relation.table_alias || o.relation.name
- collector << "#{quote_table_name join_name}.#{quote_column_name o.name}"
+ collector << quote_table_name(join_name) << "." << quote_column_name(o.name)
end
- alias :visit_Arel_Attributes_Integer :visit_Arel_Attributes_Attribute
- alias :visit_Arel_Attributes_Float :visit_Arel_Attributes_Attribute
- alias :visit_Arel_Attributes_Decimal :visit_Arel_Attributes_Attribute
- alias :visit_Arel_Attributes_String :visit_Arel_Attributes_Attribute
- alias :visit_Arel_Attributes_Time :visit_Arel_Attributes_Attribute
- alias :visit_Arel_Attributes_Boolean :visit_Arel_Attributes_Attribute
def literal(o, collector); collector << o.to_s; end
@@ -785,14 +778,11 @@ module Arel # :nodoc: all
end
def inject_join(list, collector, join_str)
- len = list.length - 1
- list.each_with_index.inject(collector) { |c, (x, i)|
- if i == len
- visit x, c
- else
- visit(x, c) << join_str
- end
- }
+ list.each_with_index do |x, i|
+ collector << join_str unless i == 0
+ collector = visit(x, collector)
+ end
+ collector
end
def unboundable?(value)
diff --git a/activerecord/lib/arel/visitors/visitor.rb b/activerecord/lib/arel/visitors/visitor.rb
index 1c17184e86..9066307aed 100644
--- a/activerecord/lib/arel/visitors/visitor.rb
+++ b/activerecord/lib/arel/visitors/visitor.rb
@@ -7,16 +7,15 @@ module Arel # :nodoc: all
@dispatch = get_dispatch_cache
end
- def accept(object, *args)
- visit object, *args
+ def accept(object, collector = nil)
+ visit object, collector
end
private
-
attr_reader :dispatch
def self.dispatch_cache
- Hash.new do |hash, klass|
+ @dispatch_cache ||= Hash.new do |hash, klass|
hash[klass] = "visit_#{(klass.name || '').gsub('::', '_')}"
end
end
@@ -25,9 +24,13 @@ module Arel # :nodoc: all
self.class.dispatch_cache
end
- def visit(object, *args)
+ def visit(object, collector = nil)
dispatch_method = dispatch[object.class]
- send dispatch_method, object, *args
+ if collector
+ send dispatch_method, object, collector
+ else
+ send dispatch_method, object
+ end
rescue NoMethodError => e
raise e if respond_to?(dispatch_method, true)
superklass = object.class.ancestors.find { |klass|
diff --git a/activerecord/lib/arel/visitors/where_sql.rb b/activerecord/lib/arel/visitors/where_sql.rb
index c6caf5e7c9..8fb299d1c8 100644
--- a/activerecord/lib/arel/visitors/where_sql.rb
+++ b/activerecord/lib/arel/visitors/where_sql.rb
@@ -9,7 +9,6 @@ module Arel # :nodoc: all
end
private
-
def visit_Arel_Nodes_SelectCore(o, collector)
collector << "WHERE "
wheres = o.wheres.map do |where|
diff --git a/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb b/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb
index 35d5664400..56b9628a92 100644
--- a/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb
@@ -13,7 +13,6 @@ module ActiveRecord
end
private
-
def application_record_file_name
@application_record_file_name ||=
if namespaced?
diff --git a/activerecord/lib/rails/generators/active_record/migration.rb b/activerecord/lib/rails/generators/active_record/migration.rb
index cbb88d571d..af753071a9 100644
--- a/activerecord/lib/rails/generators/active_record/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration.rb
@@ -17,7 +17,6 @@ module ActiveRecord
end
private
-
def primary_key_type
key_type = options[:primary_key_type]
", id: :#{key_type}" if key_type
diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
index c71bbdcab8..d4733f948f 100644
--- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
@@ -35,7 +35,6 @@ module ActiveRecord
hook_for :test_framework
private
-
def attributes_with_index
attributes.select { |a| !a.reference? && a.has_index? }
end
diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
index f977b2997b..f1f457aedd 100644
--- a/activerecord/test/active_record/connection_adapters/fake_adapter.rb
+++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
@@ -32,7 +32,8 @@ module ActiveRecord
name.to_s,
options[:default],
fetch_type_metadata(sql_type),
- options[:null])
+ options[:null],
+ )
end
def columns(table_name)
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index ce2ed06c1d..0bc617edbe 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -12,6 +12,7 @@ module ActiveRecord
def setup
@connection = ActiveRecord::Base.connection
@connection.materialize_transactions
+ @connection_handler = ActiveRecord::Base.connection_handler
end
##
@@ -166,7 +167,7 @@ module ActiveRecord
def test_preventing_writes_predicate
assert_not_predicate @connection, :preventing_writes?
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_predicate @connection, :preventing_writes?
end
@@ -176,7 +177,7 @@ module ActiveRecord
def test_errors_when_an_insert_query_is_called_while_preventing_writes
assert_no_queries do
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.transaction do
@connection.insert("INSERT INTO subscribers(nick) VALUES ('138853948594')", nil, false)
end
@@ -190,7 +191,7 @@ module ActiveRecord
assert_no_queries do
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.transaction do
@connection.update("UPDATE subscribers SET nick = '9989' WHERE nick = '138853948594'")
end
@@ -204,7 +205,7 @@ module ActiveRecord
assert_no_queries do
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.transaction do
@connection.delete("DELETE FROM subscribers WHERE nick = '138853948594'")
end
@@ -216,7 +217,7 @@ module ActiveRecord
def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
@connection.insert("INSERT INTO subscribers(nick) VALUES ('138853948594')")
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
result = @connection.select_all("SELECT subscribers.* FROM subscribers WHERE nick = '138853948594'")
assert_equal 1, result.length
end
@@ -559,7 +560,6 @@ module ActiveRecord
end
private
-
def reset_fixtures(*fixture_names)
ActiveRecord::FixtureSet.reset_cache
diff --git a/activerecord/test/cases/adapters/mysql2/annotate_test.rb b/activerecord/test/cases/adapters/mysql2/annotate_test.rb
deleted file mode 100644
index b512540073..0000000000
--- a/activerecord/test/cases/adapters/mysql2/annotate_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require "cases/helper"
-require "models/post"
-
-class Mysql2AnnotateTest < ActiveRecord::Mysql2TestCase
- fixtures :posts
-
- def test_annotate_wraps_content_in_an_inline_comment
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/}) do
- posts = Post.select(:id).annotate("foo")
- assert posts.first
- end
- end
-
- def test_annotate_is_sanitized
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/}) do
- posts = Post.select(:id).annotate("*/foo/*")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/}) do
- posts = Post.select(:id).annotate("**//foo//**")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/ /\* bar \*/}) do
- posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* \+ MAX_EXECUTION_TIME\(1\) \*/}) do
- posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
- assert posts.first
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
index c32475c683..3756f74c95 100644
--- a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
@@ -22,7 +22,7 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
CollationTest.validates_uniqueness_of(:string_ci_column, case_sensitive: false)
CollationTest.create!(string_ci_column: "A")
invalid = CollationTest.new(string_ci_column: "a")
- queries = assert_sql { invalid.save }
+ queries = capture_sql { invalid.save }
ci_uniqueness_query = queries.detect { |q| q.match(/string_ci_column/) }
assert_no_match(/lower/i, ci_uniqueness_query)
end
@@ -31,7 +31,7 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
CollationTest.validates_uniqueness_of(:string_cs_column, case_sensitive: false)
CollationTest.create!(string_cs_column: "A")
invalid = CollationTest.new(string_cs_column: "a")
- queries = assert_sql { invalid.save }
+ queries = capture_sql { invalid.save }
cs_uniqueness_query = queries.detect { |q| q.match(/string_cs_column/) }
assert_match(/lower/i, cs_uniqueness_query)
end
@@ -40,7 +40,7 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
CollationTest.validates_uniqueness_of(:string_ci_column, case_sensitive: true)
CollationTest.create!(string_ci_column: "A")
invalid = CollationTest.new(string_ci_column: "A")
- queries = assert_sql { invalid.save }
+ queries = capture_sql { invalid.save }
ci_uniqueness_query = queries.detect { |q| q.match(/string_ci_column/) }
assert_match(/binary/i, ci_uniqueness_query)
end
@@ -49,7 +49,7 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
CollationTest.validates_uniqueness_of(:string_cs_column, case_sensitive: true)
CollationTest.create!(string_cs_column: "A")
invalid = CollationTest.new(string_cs_column: "A")
- queries = assert_sql { invalid.save }
+ queries = capture_sql { invalid.save }
cs_uniqueness_query = queries.detect { |q| q.match(/string_cs_column/) }
assert_no_match(/binary/i, cs_uniqueness_query)
end
@@ -58,7 +58,7 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
CollationTest.validates_uniqueness_of(:binary_column, case_sensitive: true)
CollationTest.create!(binary_column: "A")
invalid = CollationTest.new(binary_column: "A")
- queries = assert_sql { invalid.save }
+ queries = capture_sql { invalid.save }
bin_uniqueness_query = queries.detect { |q| q.match(/binary_column/) }
assert_no_match(/\bBINARY\b/, bin_uniqueness_query)
end
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 9c6566106a..cb7461a8d5 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -197,7 +197,6 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
end
private
-
def test_lock_free(lock_name)
@connection.select_value("SELECT IS_FREE_LOCK(#{@connection.quote(lock_name)})") == 1
end
diff --git a/activerecord/test/cases/adapters/mysql2/enum_test.rb b/activerecord/test/cases/adapters/mysql2/enum_test.rb
index 832f5d61d1..1168b3677e 100644
--- a/activerecord/test/cases/adapters/mysql2/enum_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/enum_test.rb
@@ -1,11 +1,20 @@
# frozen_string_literal: true
require "cases/helper"
+require "support/schema_dumping_helper"
class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
+
class EnumTest < ActiveRecord::Base
end
+ def setup
+ EnumTest.connection.create_table :enum_tests, id: false, force: true do |t|
+ t.column :enum_column, "enum('text','blob','tiny','medium','long','unsigned','bigint')"
+ end
+ end
+
def test_enum_limit
column = EnumTest.columns_hash["enum_column"]
assert_equal 8, column.limit
@@ -20,4 +29,9 @@ class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
column = EnumTest.columns_hash["enum_column"]
assert_not_predicate column, :bigint?
end
+
+ def test_schema_dumping
+ schema = dump_table_schema "enum_tests"
+ assert_match %r{t\.column "enum_column", "enum\('text','blob','tiny','medium','long','unsigned','bigint'\)"$}, schema
+ end
end
diff --git a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
index 6ade2eec24..cfc1823773 100644
--- a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
@@ -8,6 +8,7 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
def setup
@conn = ActiveRecord::Base.connection
+ @connection_handler = ActiveRecord::Base.connection_handler
end
def test_exec_query_nothing_raises_with_no_result_queries
@@ -19,6 +20,18 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
end
end
+ def test_database_exists_returns_false_if_database_does_not_exist
+ config = ActiveRecord::Base.configurations["arunit"].merge(database: "inexistent_activerecord_unittest")
+ assert_not ActiveRecord::ConnectionAdapters::Mysql2Adapter.database_exists?(config),
+ "expected database to not exist"
+ end
+
+ def test_database_exists_returns_true_when_the_database_exists
+ config = ActiveRecord::Base.configurations["arunit"]
+ assert ActiveRecord::ConnectionAdapters::Mysql2Adapter.database_exists?(config),
+ "expected database #{config[:database]} to exist"
+ end
+
def test_columns_for_distinct_zero_orders
assert_equal "posts.id",
@conn.columns_for_distinct("posts.id", [])
@@ -148,7 +161,7 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
def test_errors_when_an_insert_query_is_called_while_preventing_writes
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.insert("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
end
end
@@ -158,7 +171,7 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
@conn.insert("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.update("UPDATE `engines` SET `engines`.`car_id` = '9989' WHERE `engines`.`car_id` = '138853948594'")
end
end
@@ -168,7 +181,7 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("DELETE FROM `engines` where `engines`.`car_id` = '138853948594'")
end
end
@@ -178,7 +191,7 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("REPLACE INTO `engines` SET `engines`.`car_id` = '249823948'")
end
end
@@ -187,19 +200,19 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @conn.execute("SELECT `engines`.* FROM `engines` WHERE `engines`.`car_id` = '138853948594'").entries.count
end
end
def test_doesnt_error_when_a_show_query_is_called_while_preventing_writes
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 2, @conn.execute("SHOW FULL FIELDS FROM `engines`").entries.count
end
end
def test_doesnt_error_when_a_set_query_is_called_while_preventing_writes
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_nil @conn.execute("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci")
end
end
@@ -207,13 +220,27 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
def test_doesnt_error_when_a_read_query_with_leading_chars_is_called_while_preventing_writes
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @conn.execute("(\n( SELECT `engines`.* FROM `engines` WHERE `engines`.`car_id` = '138853948594' ) )").entries.count
end
end
- private
+ def test_read_timeout_exception
+ ActiveRecord::Base.establish_connection(
+ ActiveRecord::Base.configurations[:arunit].merge("read_timeout" => 1)
+ )
+ error = assert_raises(ActiveRecord::AdapterTimeout) do
+ ActiveRecord::Base.connection.execute("SELECT SLEEP(2)")
+ end
+ assert_kind_of ActiveRecord::QueryAborted, error
+
+ assert_equal Mysql2::Error::TimeoutError, error.cause.class
+ ensure
+ ActiveRecord::Base.establish_connection :arunit
+ end
+
+ private
def with_example_table(definition = "id int auto_increment primary key, number int, data varchar(255)", &block)
super(@conn, "ex", definition, &block)
end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
index d7d9a2d732..182d5a3e58 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
@@ -40,7 +40,6 @@ class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase
end
private
-
def with_encoding_utf8mb4
database_name = connection.current_database
database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'")
diff --git a/activerecord/test/cases/adapters/mysql2/set_test.rb b/activerecord/test/cases/adapters/mysql2/set_test.rb
new file mode 100644
index 0000000000..89107e142f
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/set_test.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "support/schema_dumping_helper"
+
+class Mysql2SetTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
+
+ class SetTest < ActiveRecord::Base
+ end
+
+ def setup
+ SetTest.connection.create_table :set_tests, id: false, force: true do |t|
+ t.column :set_column, "set('text','blob','tiny','medium','long','unsigned','bigint')"
+ end
+ end
+
+ def test_should_not_be_unsigned
+ column = SetTest.columns_hash["set_column"]
+ assert_not_predicate column, :unsigned?
+ end
+
+ def test_should_not_be_bigint
+ column = SetTest.columns_hash["set_column"]
+ assert_not_predicate column, :bigint?
+ end
+
+ def test_schema_dumping
+ schema = dump_table_schema "set_tests"
+ assert_match %r{t\.column "set_column", "set\('text','blob','tiny','medium','long','unsigned','bigint'\)"$}, schema
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/table_options_test.rb b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
index 1c92df940f..13cf1daa08 100644
--- a/activerecord/test/cases/adapters/mysql2/table_options_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
@@ -73,7 +73,7 @@ class Mysql2DefaultEngineOptionSchemaDumpTest < ActiveRecord::Mysql2TestCase
end
end.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], ActiveRecord::Base.connection.schema_migration).migrate
output = dump_table_schema("mysql_table_options")
options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
@@ -112,7 +112,7 @@ class Mysql2DefaultEngineOptionSqlOutputTest < ActiveRecord::Mysql2TestCase
end
end.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], ActiveRecord::Base.connection.schema_migration).migrate
assert_match %r{ENGINE=InnoDB}, @log.string
end
diff --git a/activerecord/test/cases/adapters/mysql2/transaction_test.rb b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
index 52e283f247..2041cc308f 100644
--- a/activerecord/test/cases/adapters/mysql2/transaction_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
@@ -92,7 +92,7 @@ module ActiveRecord
test "raises StatementTimeout when statement timeout exceeded" do
skip unless ActiveRecord::Base.connection.show_variable("max_execution_time")
- assert_raises(ActiveRecord::StatementTimeout) do
+ error = assert_raises(ActiveRecord::StatementTimeout) do
s = Sample.create!(value: 1)
latch1 = Concurrent::CountDownLatch.new
latch2 = Concurrent::CountDownLatch.new
@@ -117,10 +117,11 @@ module ActiveRecord
thread.join
end
end
+ assert_kind_of ActiveRecord::QueryAborted, error
end
test "raises QueryCanceled when canceling statement due to user request" do
- assert_raises(ActiveRecord::QueryCanceled) do
+ error = assert_raises(ActiveRecord::QueryCanceled) do
s = Sample.create!(value: 1)
latch = Concurrent::CountDownLatch.new
@@ -144,6 +145,7 @@ module ActiveRecord
thread.join
end
end
+ assert_kind_of ActiveRecord::QueryAborted, error
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/annotate_test.rb b/activerecord/test/cases/adapters/postgresql/annotate_test.rb
deleted file mode 100644
index 42a2861511..0000000000
--- a/activerecord/test/cases/adapters/postgresql/annotate_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require "cases/helper"
-require "models/post"
-
-class PostgresqlAnnotateTest < ActiveRecord::PostgreSQLTestCase
- fixtures :posts
-
- def test_annotate_wraps_content_in_an_inline_comment
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("foo")
- assert posts.first
- end
- end
-
- def test_annotate_is_sanitized
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("*/foo/*")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("**//foo//**")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/ /\* bar \*/}) do
- posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* \+ MAX_EXECUTION_TIME\(1\) \*/}) do
- posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
- assert posts.first
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 9494863a75..dcee4fd22d 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -239,7 +239,6 @@ module ActiveRecord
end
private
-
def with_warning_suppression
log_level = @connection.client_min_messages
@connection.client_min_messages = "error"
diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
index 0fd7b2c6ed..16baa8933d 100644
--- a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
@@ -27,20 +27,20 @@ class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
ActiveRecord::Base.table_name_prefix = "p_"
ActiveRecord::Base.table_name_suffix = "_s"
- ActiveRecord::SchemaMigration.reset_table_name
+ @connection.schema_migration.reset_table_name
ActiveRecord::InternalMetadata.reset_table_name
- ActiveRecord::SchemaMigration.delete_all rescue nil
+ @connection.schema_migration.delete_all rescue nil
ActiveRecord::Migration.verbose = false
end
def teardown
- ActiveRecord::SchemaMigration.delete_all rescue nil
+ @connection.schema_migration.delete_all rescue nil
ActiveRecord::Migration.verbose = true
ActiveRecord::Base.table_name_prefix = @old_table_name_prefix
ActiveRecord::Base.table_name_suffix = @old_table_name_suffix
- ActiveRecord::SchemaMigration.reset_table_name
+ @connection.schema_migration.reset_table_name
ActiveRecord::InternalMetadata.reset_table_name
super
@@ -50,7 +50,7 @@ class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
@connection.disable_extension("hstore")
migrations = [EnableHstore.new(nil, 1)]
- ActiveRecord::Migrator.new(:up, migrations).migrate
+ ActiveRecord::Migrator.new(:up, migrations, ActiveRecord::Base.connection.schema_migration).migrate
assert @connection.extension_enabled?("hstore"), "extension hstore should be enabled"
end
@@ -58,7 +58,7 @@ class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
@connection.enable_extension("hstore")
migrations = [DisableHstore.new(nil, 1)]
- ActiveRecord::Migrator.new(:up, migrations).migrate
+ ActiveRecord::Migrator.new(:up, migrations, ActiveRecord::Base.connection.schema_migration).migrate
assert_not @connection.extension_enabled?("hstore"), "extension hstore should not be enabled"
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
index 14c262f4ce..f312b6e23d 100644
--- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
@@ -361,7 +361,6 @@ class PostgreSQLGeometricTypesTest < ActiveRecord::PostgreSQLTestCase
end
private
-
def assert_column_exists(column_name)
assert connection.column_exists?(table_name, column_name)
end
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index 1aa0348879..ff2ab22a80 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -54,8 +54,12 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
type = PostgresqlMoney.type_for_attribute("wealth")
assert_equal(12345678.12, type.cast(+"$12,345,678.12"))
assert_equal(12345678.12, type.cast(+"$12.345.678,12"))
+ assert_equal(12345678.12, type.cast(+"12,345,678.12"))
+ assert_equal(12345678.12, type.cast(+"12.345.678,12"))
assert_equal(-1.15, type.cast(+"-$1.15"))
assert_equal(-2.25, type.cast(+"($2.25)"))
+ assert_equal(-1.15, type.cast(+"-1.15"))
+ assert_equal(-2.25, type.cast(+"(2.25)"))
end
def test_schema_dumping
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index fbd3cbf90f..830c0892d3 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -13,6 +13,7 @@ module ActiveRecord
def setup
@connection = ActiveRecord::Base.connection
+ @connection_handler = ActiveRecord::Base.connection_handler
end
def test_bad_connection
@@ -23,6 +24,18 @@ module ActiveRecord
end
end
+ def test_database_exists_returns_false_when_the_database_does_not_exist
+ config = { database: "non_extant_database", adapter: "postgresql" }
+ assert_not ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.database_exists?(config),
+ "expected database #{config[:database]} to not exist"
+ end
+
+ def test_database_exists_returns_true_when_the_database_exists
+ config = ActiveRecord::Base.configurations["arunit"]
+ assert ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.database_exists?(config),
+ "expected database #{config[:database]} to exist"
+ end
+
def test_primary_key
with_example_table do
assert_equal "id", @connection.primary_key("ex")
@@ -240,9 +253,11 @@ module ActiveRecord
def test_expression_index
with_example_table do
- @connection.add_index "ex", "mod(id, 10), abs(number)", name: "expression"
+ expr = "mod(id, 10), abs(number)"
+ @connection.add_index "ex", expr, name: "expression"
index = @connection.indexes("ex").find { |idx| idx.name == "expression" }
- assert_equal "mod(id, 10), abs(number)", index.columns
+ assert_equal expr, index.columns
+ assert_equal true, @connection.index_exists?("ex", expr, name: "expression")
end
end
@@ -379,7 +394,7 @@ module ActiveRecord
def test_errors_when_an_insert_query_is_called_while_preventing_writes
with_example_table do
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
end
end
@@ -391,7 +406,7 @@ module ActiveRecord
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.execute("UPDATE ex SET data = '9989' WHERE data = '138853948594'")
end
end
@@ -403,7 +418,7 @@ module ActiveRecord
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.execute("DELETE FROM ex where data = '138853948594'")
end
end
@@ -414,20 +429,20 @@ module ActiveRecord
with_example_table do
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @connection.execute("SELECT * FROM ex WHERE data = '138853948594'").entries.count
end
end
end
def test_doesnt_error_when_a_show_query_is_called_while_preventing_writes
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @connection.execute("SHOW TIME ZONE").entries.count
end
end
def test_doesnt_error_when_a_set_query_is_called_while_preventing_writes
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal [], @connection.execute("SET standard_conforming_strings = on").entries
end
end
@@ -436,14 +451,13 @@ module ActiveRecord
with_example_table do
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @connection.execute("(\n( SELECT * FROM ex WHERE data = '138853948594' ) )").entries.count
end
end
end
private
-
def with_example_table(definition = "id serial primary key, number integer, data character varying(255)", &block)
super(@connection, "ex", definition, &block)
end
diff --git a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
index 96cfabf58f..a4f722c063 100644
--- a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
@@ -106,7 +106,6 @@ class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
end
private
-
def assert_transaction_is_not_broken
assert_equal 1, @connection.select_value("SELECT 1")
end
diff --git a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
index 7eccaf4aa2..fae20de086 100644
--- a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
@@ -25,7 +25,6 @@ class PostgresqlRenameTableTest < ActiveRecord::PostgreSQLTestCase
end
private
-
def num_indices_named(name)
@connection.execute(<<~SQL).values.length
SELECT 1 FROM "pg_index"
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 336cec30ca..fe6a3deff4 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -104,7 +104,11 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
end
def test_schema_names
- assert_equal ["public", "test_schema", "test_schema2"], @connection.schema_names
+ schema_names = @connection.schema_names
+ assert_includes schema_names, "public"
+ assert_includes schema_names, "test_schema"
+ assert_includes schema_names, "test_schema2"
+ assert_includes schema_names, "hint_plan" if @connection.supports_optimizer_hints?
end
def test_create_schema
diff --git a/activerecord/test/cases/adapters/postgresql/transaction_test.rb b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
index 919ff3d158..311863a418 100644
--- a/activerecord/test/cases/adapters/postgresql/transaction_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
@@ -177,7 +177,6 @@ module ActiveRecord
end
private
-
def with_warning_suppression
log_level = ActiveRecord::Base.connection.client_min_messages
ActiveRecord::Base.connection.client_min_messages = "error"
diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
index d2d8ea8042..a1c985fc71 100644
--- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -293,14 +293,16 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase
create_table("pg_uuids_4", id: :uuid)
end
end.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], ActiveRecord::Base.connection.schema_migration).migrate
schema = dump_table_schema "pg_uuids_4"
assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema)
ensure
drop_table "pg_uuids_4"
ActiveRecord::Migration.verbose = @verbose_was
+ ActiveRecord::Base.connection.schema_migration.delete_all
end
+ uses_transaction :test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration
end
class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase
@@ -341,14 +343,16 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase
create_table("pg_uuids_4", id: :uuid, default: nil)
end
end.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], ActiveRecord::Base.connection.schema_migration).migrate
schema = dump_table_schema "pg_uuids_4"
assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: nil/, schema)
ensure
drop_table "pg_uuids_4"
ActiveRecord::Migration.verbose = @verbose_was
+ ActiveRecord::Base.connection.schema_migration.delete_all
end
+ uses_transaction :test_schema_dumper_for_uuid_primary_key_with_default_nil_in_legacy_migration
end
class PostgresqlUUIDTestInverseOf < ActiveRecord::PostgreSQLTestCase
diff --git a/activerecord/test/cases/adapters/sqlite3/annotate_test.rb b/activerecord/test/cases/adapters/sqlite3/annotate_test.rb
deleted file mode 100644
index 6567a5eca3..0000000000
--- a/activerecord/test/cases/adapters/sqlite3/annotate_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require "cases/helper"
-require "models/post"
-
-class SQLite3AnnotateTest < ActiveRecord::SQLite3TestCase
- fixtures :posts
-
- def test_annotate_wraps_content_in_an_inline_comment
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("foo")
- assert posts.first
- end
- end
-
- def test_annotate_is_sanitized
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("*/foo/*")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("**//foo//**")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/ /\* bar \*/}) do
- posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* \+ MAX_EXECUTION_TIME\(1\) \*/}) do
- posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
- assert posts.first
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/sqlite3/collation_test.rb b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
index 76c8f7d8dd..d938b5ff2f 100644
--- a/activerecord/test/cases/adapters/sqlite3/collation_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
@@ -11,6 +11,10 @@ class SQLite3CollationTest < ActiveRecord::SQLite3TestCase
@connection.create_table :collation_table_sqlite3, force: true do |t|
t.string :string_nocase, collation: "NOCASE"
t.text :text_rtrim, collation: "RTRIM"
+ # The decimal column might interfere with collation parsing.
+ # Thus, add this column type and some other string column afterwards.
+ t.decimal :decimal_col, precision: 6, scale: 2
+ t.string :string_after_decimal_nocase, collation: "NOCASE"
end
end
@@ -22,6 +26,11 @@ class SQLite3CollationTest < ActiveRecord::SQLite3TestCase
column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == "string_nocase" }
assert_equal :string, column.type
assert_equal "NOCASE", column.collation
+
+ # Verify collation of a column behind the decimal column as well.
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == "string_after_decimal_nocase" }
+ assert_equal :string, column.type
+ assert_equal "NOCASE", column.collation
end
test "text column with collation" do
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 806cfbfc00..b6d72c7bcd 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -19,6 +19,8 @@ module ActiveRecord
@conn = Base.sqlite3_connection database: ":memory:",
adapter: "sqlite3",
timeout: 100
+
+ @connection_handler = ActiveRecord::Base.connection_handler
end
def test_bad_connection
@@ -28,6 +30,17 @@ module ActiveRecord
end
end
+ def test_database_exists_returns_false_when_the_database_does_not_exist
+ assert_not SQLite3Adapter.database_exists?(adapter: "sqlite3", database: "non_extant_db"),
+ "expected non_extant_db to not exist"
+ end
+
+ def test_database_exists_returns_true_when_databae_exists
+ config = ActiveRecord::Base.configurations["arunit"]
+ assert SQLite3Adapter.database_exists?(config),
+ "expected #{config[:database]} to exist"
+ end
+
unless in_memory_db?
def test_connect_with_url
original_connection = ActiveRecord::Base.remove_connection
@@ -51,6 +64,11 @@ module ActiveRecord
end
end
+ def test_database_exists_returns_true_for_an_in_memory_db
+ assert SQLite3Adapter.database_exists?(database: ":memory:"),
+ "Expected in memory database to exist"
+ end
+
def test_column_types
owner = Owner.create!(name: "hello".encode("ascii-8bit"))
owner.reload
@@ -572,7 +590,7 @@ module ActiveRecord
def test_errors_when_an_insert_query_is_called_while_preventing_writes
with_example_table "id int, data string" do
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
end
end
@@ -584,7 +602,7 @@ module ActiveRecord
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("UPDATE ex SET data = '9989' WHERE data = '138853948594'")
end
end
@@ -596,7 +614,7 @@ module ActiveRecord
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("DELETE FROM ex where data = '138853948594'")
end
end
@@ -608,7 +626,7 @@ module ActiveRecord
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("REPLACE INTO ex (data) VALUES ('249823948')")
end
end
@@ -619,7 +637,7 @@ module ActiveRecord
with_example_table "id int, data string" do
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @conn.execute("SELECT data from ex WHERE data = '138853948594'").count
end
end
@@ -629,14 +647,13 @@ module ActiveRecord
with_example_table "id int, data string" do
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @conn.execute(" SELECT data from ex WHERE data = '138853948594'").count
end
end
end
private
-
def assert_logged(logs)
subscriber = SQLSubscriber.new
subscription = ActiveSupport::Notifications.subscribe("sql.active_record", subscriber)
diff --git a/activerecord/test/cases/annotate_test.rb b/activerecord/test/cases/annotate_test.rb
new file mode 100644
index 0000000000..4d71d28f83
--- /dev/null
+++ b/activerecord/test/cases/annotate_test.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/post"
+
+class AnnotateTest < ActiveRecord::TestCase
+ fixtures :posts
+
+ def test_annotate_wraps_content_in_an_inline_comment
+ quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
+ posts = Post.select(:id).annotate("foo")
+ assert posts.first
+ end
+ end
+
+ def test_annotate_is_sanitized
+ quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
+ posts = Post.select(:id).annotate("*/foo/*")
+ assert posts.first
+ end
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
+ posts = Post.select(:id).annotate("**//foo//**")
+ assert posts.first
+ end
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/ /\* bar \*/}i) do
+ posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
+ assert posts.first
+ end
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \+ MAX_EXECUTION_TIME\(1\) \*/}i) do
+ posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
+ assert posts.first
+ end
+ end
+
+ private
+ def regexp_escape_table_name(name)
+ Regexp.escape(Post.connection.quote_table_name(name))
+ end
+end
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index 7aa6d089c5..2d5a06a4ac 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -9,7 +9,8 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
@original_verbose = ActiveRecord::Migration.verbose
ActiveRecord::Migration.verbose = false
@connection = ActiveRecord::Base.connection
- ActiveRecord::SchemaMigration.drop_table
+ @schema_migration = @connection.schema_migration
+ @schema_migration.drop_table
end
teardown do
@@ -18,21 +19,21 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
@connection.drop_table :nep_schema_migrations rescue nil
@connection.drop_table :has_timestamps rescue nil
@connection.drop_table :multiple_indexes rescue nil
- ActiveRecord::SchemaMigration.delete_all rescue nil
+ @schema_migration.delete_all rescue nil
ActiveRecord::Migration.verbose = @original_verbose
end
def test_has_primary_key
old_primary_key_prefix_type = ActiveRecord::Base.primary_key_prefix_type
ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
- assert_equal "version", ActiveRecord::SchemaMigration.primary_key
+ assert_equal "version", @schema_migration.primary_key
- ActiveRecord::SchemaMigration.create_table
- assert_difference "ActiveRecord::SchemaMigration.count", 1 do
- ActiveRecord::SchemaMigration.create version: 12
+ @schema_migration.create_table
+ assert_difference "@schema_migration.count", 1 do
+ @schema_migration.create version: 12
end
ensure
- ActiveRecord::SchemaMigration.drop_table
+ @schema_migration.drop_table
ActiveRecord::Base.primary_key_prefix_type = old_primary_key_prefix_type
end
@@ -54,7 +55,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
def test_schema_define_with_table_name_prefix
old_table_name_prefix = ActiveRecord::Base.table_name_prefix
ActiveRecord::Base.table_name_prefix = "nep_"
- ActiveRecord::SchemaMigration.reset_table_name
+ @schema_migration.reset_table_name
ActiveRecord::InternalMetadata.reset_table_name
ActiveRecord::Schema.define(version: 7) do
create_table :fruits do |t|
@@ -67,7 +68,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
assert_equal 7, @connection.migration_context.current_version
ensure
ActiveRecord::Base.table_name_prefix = old_table_name_prefix
- ActiveRecord::SchemaMigration.reset_table_name
+ @schema_migration.reset_table_name
ActiveRecord::InternalMetadata.reset_table_name
end
@@ -89,10 +90,10 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
end
def test_normalize_version
- assert_equal "118", ActiveRecord::SchemaMigration.normalize_migration_number("0000118")
- assert_equal "002", ActiveRecord::SchemaMigration.normalize_migration_number("2")
- assert_equal "017", ActiveRecord::SchemaMigration.normalize_migration_number("0017")
- assert_equal "20131219224947", ActiveRecord::SchemaMigration.normalize_migration_number("20131219224947")
+ assert_equal "118", @schema_migration.normalize_migration_number("0000118")
+ assert_equal "002", @schema_migration.normalize_migration_number("2")
+ assert_equal "017", @schema_migration.normalize_migration_number("0017")
+ assert_equal "20131219224947", @schema_migration.normalize_migration_number("20131219224947")
end
def test_schema_load_with_multiple_indexes_for_column_of_different_names
diff --git a/activerecord/test/cases/arel/attributes/attribute_test.rb b/activerecord/test/cases/arel/attributes/attribute_test.rb
index c7bd0a053b..7ebb90c6fd 100644
--- a/activerecord/test/cases/arel/attributes/attribute_test.rb
+++ b/activerecord/test/cases/arel/attributes/attribute_test.rb
@@ -638,6 +638,18 @@ module Arel
)
end
+ if Gem::Version.new("2.7.0") <= Gem::Version.new(RUBY_VERSION)
+ it "can be constructed with a range implicitly starting at Infinity" do
+ attribute = Attribute.new nil, nil
+ node = attribute.between(eval("..0")) # eval for backwards compatibility
+
+ node.must_equal Nodes::LessThanOrEqual.new(
+ attribute,
+ Nodes::Casted.new(0, attribute)
+ )
+ end
+ end
+
if Gem::Version.new("2.6.0") <= Gem::Version.new(RUBY_VERSION)
it "can be constructed with a range implicitly ending at Infinity" do
attribute = Attribute.new nil, nil
@@ -839,6 +851,18 @@ module Arel
)
end
+ if Gem::Version.new("2.7.0") <= Gem::Version.new(RUBY_VERSION)
+ it "can be constructed with a range implicitly starting at Infinity" do
+ attribute = Attribute.new nil, nil
+ node = attribute.not_between(eval("..0")) # eval for backwards compatibility
+
+ node.must_equal Nodes::GreaterThan.new(
+ attribute,
+ Nodes::Casted.new(0, attribute)
+ )
+ end
+ end
+
if Gem::Version.new("2.6.0") <= Gem::Version.new(RUBY_VERSION)
it "can be constructed with a range implicitly ending at Infinity" do
attribute = Attribute.new nil, nil
diff --git a/activerecord/test/cases/arel/attributes_test.rb b/activerecord/test/cases/arel/attributes_test.rb
index b00af4bd29..1712633ae9 100644
--- a/activerecord/test/cases/arel/attributes_test.rb
+++ b/activerecord/test/cases/arel/attributes_test.rb
@@ -23,46 +23,5 @@ module Arel
assert_equal 2, array.uniq.size
end
end
-
- describe "for" do
- it "deals with unknown column types" do
- column = Struct.new(:type).new :crazy
- Attributes.for(column).must_equal Attributes::Undefined
- end
-
- it "returns the correct constant for strings" do
- [:string, :text, :binary].each do |type|
- column = Struct.new(:type).new type
- Attributes.for(column).must_equal Attributes::String
- end
- end
-
- it "returns the correct constant for ints" do
- column = Struct.new(:type).new :integer
- Attributes.for(column).must_equal Attributes::Integer
- end
-
- it "returns the correct constant for floats" do
- column = Struct.new(:type).new :float
- Attributes.for(column).must_equal Attributes::Float
- end
-
- it "returns the correct constant for decimals" do
- column = Struct.new(:type).new :decimal
- Attributes.for(column).must_equal Attributes::Decimal
- end
-
- it "returns the correct constant for boolean" do
- column = Struct.new(:type).new :boolean
- Attributes.for(column).must_equal Attributes::Boolean
- end
-
- it "returns the correct constant for time" do
- [:date, :datetime, :timestamp, :time].each do |type|
- column = Struct.new(:type).new type
- Attributes.for(column).must_equal Attributes::Time
- end
- end
- end
end
end
diff --git a/activerecord/test/cases/arel/nodes/node_test.rb b/activerecord/test/cases/arel/nodes/node_test.rb
index f4f07ef2c5..f1e0ce1ea9 100644
--- a/activerecord/test/cases/arel/nodes/node_test.rb
+++ b/activerecord/test/cases/arel/nodes/node_test.rb
@@ -18,24 +18,5 @@ module Arel
assert klass.ancestors.include?(Nodes::Node), klass.name
end
end
-
- def test_each
- list = []
- node = Nodes::Node.new
- node.each { |n| list << n }
- assert_equal [node], list
- end
-
- def test_generator
- list = []
- node = Nodes::Node.new
- node.each.each { |n| list << n }
- assert_equal [node], list
- end
-
- def test_enumerable
- node = Nodes::Node.new
- assert_kind_of Enumerable, node
- end
end
end
diff --git a/activerecord/test/cases/arel/select_manager_test.rb b/activerecord/test/cases/arel/select_manager_test.rb
index e6c49cd429..526fe6787a 100644
--- a/activerecord/test/cases/arel/select_manager_test.rb
+++ b/activerecord/test/cases/arel/select_manager_test.rb
@@ -369,16 +369,6 @@ module Arel
mgr = table.from
assert mgr.ast
end
-
- it "should allow orders to work when the ast is grepped" do
- table = Table.new :users
- mgr = table.from
- mgr.project Arel.sql "*"
- mgr.from table
- mgr.orders << Arel::Nodes::Ascending.new(Arel.sql("foo"))
- mgr.ast.grep(Arel::Nodes::OuterJoin)
- mgr.to_sql.must_be_like %{ SELECT * FROM "users" ORDER BY foo ASC }
- end
end
describe "taken" do
diff --git a/activerecord/test/cases/arel/visitors/depth_first_test.rb b/activerecord/test/cases/arel/visitors/depth_first_test.rb
deleted file mode 100644
index 106be2311d..0000000000
--- a/activerecord/test/cases/arel/visitors/depth_first_test.rb
+++ /dev/null
@@ -1,276 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "../helper"
-
-module Arel
- module Visitors
- class TestDepthFirst < Arel::Test
- Collector = Struct.new(:calls) do
- def call(object)
- calls << object
- end
- end
-
- def setup
- @collector = Collector.new []
- @visitor = Visitors::DepthFirst.new @collector
- end
-
- def test_raises_with_object
- assert_raises(TypeError) do
- @visitor.accept(Object.new)
- end
- end
-
-
- # unary ops
- [
- Arel::Nodes::Not,
- Arel::Nodes::Group,
- Arel::Nodes::On,
- Arel::Nodes::Grouping,
- Arel::Nodes::Offset,
- Arel::Nodes::Ordering,
- Arel::Nodes::StringJoin,
- Arel::Nodes::UnqualifiedColumn,
- Arel::Nodes::ValuesList,
- Arel::Nodes::Limit,
- Arel::Nodes::Else,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- op = klass.new(:a)
- @visitor.accept op
- assert_equal [:a, op], @collector.calls
- end
- end
-
- # functions
- [
- Arel::Nodes::Exists,
- Arel::Nodes::Avg,
- Arel::Nodes::Min,
- Arel::Nodes::Max,
- Arel::Nodes::Sum,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- func = klass.new(:a, "b")
- @visitor.accept func
- assert_equal [:a, "b", false, func], @collector.calls
- end
- end
-
- def test_named_function
- func = Arel::Nodes::NamedFunction.new(:a, :b, "c")
- @visitor.accept func
- assert_equal [:a, :b, false, "c", func], @collector.calls
- end
-
- def test_lock
- lock = Nodes::Lock.new true
- @visitor.accept lock
- assert_equal [lock], @collector.calls
- end
-
- def test_count
- count = Nodes::Count.new :a, :b, "c"
- @visitor.accept count
- assert_equal [:a, "c", :b, count], @collector.calls
- end
-
- def test_inner_join
- join = Nodes::InnerJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_full_outer_join
- join = Nodes::FullOuterJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_outer_join
- join = Nodes::OuterJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_right_outer_join
- join = Nodes::RightOuterJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_comment
- comment = Nodes::Comment.new ["foo"]
- @visitor.accept comment
- assert_equal ["foo", ["foo"], comment], @collector.calls
- end
-
- [
- Arel::Nodes::Assignment,
- Arel::Nodes::Between,
- Arel::Nodes::Concat,
- Arel::Nodes::DoesNotMatch,
- Arel::Nodes::Equality,
- Arel::Nodes::GreaterThan,
- Arel::Nodes::GreaterThanOrEqual,
- Arel::Nodes::In,
- Arel::Nodes::LessThan,
- Arel::Nodes::LessThanOrEqual,
- Arel::Nodes::Matches,
- Arel::Nodes::NotEqual,
- Arel::Nodes::NotIn,
- Arel::Nodes::Or,
- Arel::Nodes::TableAlias,
- Arel::Nodes::As,
- Arel::Nodes::DeleteStatement,
- Arel::Nodes::JoinSource,
- Arel::Nodes::When,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- binary = klass.new(:a, :b)
- @visitor.accept binary
- assert_equal [:a, :b, binary], @collector.calls
- end
- end
-
- def test_Arel_Nodes_InfixOperation
- binary = Arel::Nodes::InfixOperation.new(:o, :a, :b)
- @visitor.accept binary
- assert_equal [:a, :b, binary], @collector.calls
- end
-
- # N-ary
- [
- Arel::Nodes::And,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- binary = klass.new([:a, :b, :c])
- @visitor.accept binary
- assert_equal [:a, :b, :c, binary], @collector.calls
- end
- end
-
- [
- Arel::Attributes::Integer,
- Arel::Attributes::Float,
- Arel::Attributes::String,
- Arel::Attributes::Time,
- Arel::Attributes::Boolean,
- Arel::Attributes::Attribute
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- binary = klass.new(:a, :b)
- @visitor.accept binary
- assert_equal [:a, :b, binary], @collector.calls
- end
- end
-
- def test_table
- relation = Arel::Table.new(:users)
- @visitor.accept relation
- assert_equal ["users", relation], @collector.calls
- end
-
- def test_array
- node = Nodes::Or.new(:a, :b)
- list = [node]
- @visitor.accept list
- assert_equal [:a, :b, node, list], @collector.calls
- end
-
- def test_set
- node = Nodes::Or.new(:a, :b)
- set = Set.new([node])
- @visitor.accept set
- assert_equal [:a, :b, node, set], @collector.calls
- end
-
- def test_hash
- node = Nodes::Or.new(:a, :b)
- hash = { node => node }
- @visitor.accept hash
- assert_equal [:a, :b, node, :a, :b, node, hash], @collector.calls
- end
-
- def test_update_statement
- stmt = Nodes::UpdateStatement.new
- stmt.relation = :a
- stmt.values << :b
- stmt.wheres << :c
- stmt.orders << :d
- stmt.limit = :e
-
- @visitor.accept stmt
- assert_equal [:a, :b, stmt.values, :c, stmt.wheres, :d, stmt.orders,
- :e, stmt], @collector.calls
- end
-
- def test_select_core
- core = Nodes::SelectCore.new
- core.projections << :a
- core.froms = :b
- core.wheres << :c
- core.groups << :d
- core.windows << :e
- core.havings << :f
-
- @visitor.accept core
- assert_equal [
- :a, core.projections,
- :b, [],
- core.source,
- :c, core.wheres,
- :d, core.groups,
- :e, core.windows,
- :f, core.havings,
- core], @collector.calls
- end
-
- def test_select_statement
- ss = Nodes::SelectStatement.new
- ss.cores.replace [:a]
- ss.orders << :b
- ss.limit = :c
- ss.lock = :d
- ss.offset = :e
-
- @visitor.accept ss
- assert_equal [
- :a, ss.cores,
- :b, ss.orders,
- :c,
- :d,
- :e,
- ss], @collector.calls
- end
-
- def test_insert_statement
- stmt = Nodes::InsertStatement.new
- stmt.relation = :a
- stmt.columns << :b
- stmt.values = :c
-
- @visitor.accept stmt
- assert_equal [:a, :b, stmt.columns, :c, stmt], @collector.calls
- end
-
- def test_case
- node = Arel::Nodes::Case.new
- node.case = :a
- node.conditions << :b
- node.default = :c
-
- @visitor.accept node
- assert_equal [:a, :b, node.conditions, :c, node], @collector.calls
- end
-
- def test_node
- node = Nodes::Node.new
- @visitor.accept node
- assert_equal [node], @collector.calls
- end
- end
- end
-end
diff --git a/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb b/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb
index a07a1a050a..36f9eb49a2 100644
--- a/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb
+++ b/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb
@@ -48,7 +48,13 @@ module Arel
node = Nodes::Union.new(Nodes::True.new, Nodes::False.new)
assert_equal "( TRUE UNION FALSE )", node.to_sql
- node.first # from Nodes::Node's Enumerable mixin
+ visitor = Class.new(Visitor) {
+ def visit_Arel_Nodes_Union(o); end
+ alias :visit_Arel_Nodes_True :visit_Arel_Nodes_Union
+ alias :visit_Arel_Nodes_False :visit_Arel_Nodes_Union
+ }.new
+
+ visitor.accept(node)
assert_equal "( TRUE UNION FALSE )", node.to_sql
end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index 49f754be63..cbe48a374f 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -37,8 +37,8 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_with_hmt_does_not_table_name_collide_when_joining_associations
authors = Author.joins(:posts).eager_load(:comments).where(posts: { tags_count: 1 }).order(:id).to_a
- assert_equal 3, assert_no_queries { authors.size }
- assert_equal 10, assert_no_queries { authors[0].comments.size }
+ assert_equal 3, assert_queries(0) { authors.size }
+ assert_equal 10, assert_queries(0) { authors[0].comments.size }
end
def test_eager_association_loading_grafts_stashed_associations_to_correct_parent
@@ -103,14 +103,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
firms = Firm.all.merge!(includes: { account: { firm: :account } }, order: "companies.id").to_a
assert_equal 2, firms.size
assert_equal firms.first.account, firms.first.account.firm.account
- assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account }
- assert_equal companies(:first_firm).account.firm.account, assert_no_queries { firms.first.account.firm.account }
+ assert_equal companies(:first_firm).account, assert_queries(0) { firms.first.account.firm.account }
+ assert_equal companies(:first_firm).account.firm.account, assert_queries(0) { firms.first.account.firm.account }
end
def test_eager_association_loading_with_has_many_sti
topics = Topic.all.merge!(includes: :replies, order: "topics.id").to_a
first, second, = topics(:first).replies.size, topics(:second).replies.size
- assert_no_queries do
+ assert_queries(0) do
assert_equal first, topics[0].replies.size
assert_equal second, topics[1].replies.size
end
@@ -131,13 +131,13 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
replies = Reply.all.merge!(includes: :topic, order: "topics.id").to_a
assert_includes replies, topics(:second)
assert_not_includes replies, topics(:first)
- assert_equal topics(:first), assert_no_queries { replies.first.topic }
+ assert_equal topics(:first), assert_queries(0) { replies.first.topic }
end
def test_eager_association_loading_with_multiple_stis_and_order
author = Author.all.merge!(includes: { posts: [ :special_comments, :very_special_comment ] }, order: ["authors.name", "comments.body", "very_special_comments_posts.body"], where: "posts.id = 4").first
assert_equal authors(:david), author
- assert_no_queries do
+ assert_queries(0) do
author.posts.first.special_comments
author.posts.first.very_special_comment
end
@@ -146,7 +146,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_of_stis_with_multiple_references
authors = Author.all.merge!(includes: { posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } } }, order: "comments.body, very_special_comments_posts.body", where: "posts.id = 4").to_a
assert_equal [authors(:david)], authors
- assert_no_queries do
+ assert_queries(0) do
authors.first.posts.first.special_comments.first.post.special_comments
authors.first.posts.first.special_comments.first.post.very_special_comment
end
@@ -155,14 +155,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_where_first_level_returns_nil
authors = Author.all.merge!(includes: { post_about_thinking: :comments }, order: "authors.id DESC").to_a
assert_equal [authors(:bob), authors(:mary), authors(:david)], authors
- assert_no_queries do
+ assert_queries(0) do
authors[2].post_about_thinking.comments.first
end
end
def test_preload_through_missing_records
post = Post.where.not(author_id: Author.select(:id)).preload(author: { comments: :post }).first!
- assert_no_queries { assert_nil post.author }
+ assert_queries(0) { assert_nil post.author }
end
def test_eager_association_loading_with_missing_first_record
@@ -172,12 +172,12 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through
source = Vertex.all.merge!(includes: { sinks: { sinks: { sinks: :sinks } } }, order: "vertices.id").first
- assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first }
+ assert_equal vertices(:vertex_4), assert_queries(0) { source.sinks.first.sinks.first.sinks.first }
end
def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many
sink = Vertex.all.merge!(includes: { sources: { sources: { sources: :sources } } }, order: "vertices.id DESC").first
- assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first }
+ assert_equal vertices(:vertex_1), assert_queries(0) { sink.sources.first.sources.first.sources.first.sources.first }
end
def test_eager_association_loading_with_cascaded_interdependent_one_level_and_two_levels
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
index 849939de75..9be21b23db 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -14,6 +14,7 @@ module Remembered
included do
after_create :remember
+
private
def remember; self.class.remembered << self; end
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 9ed25ca7c2..cb46f9e053 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -523,7 +523,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_order_string_with_quoted_table_name
quoted_posts_id = Comment.connection.quote_table_name("posts") + "." + Comment.connection.quote_column_name("id")
assert_nothing_raised do
- Comment.includes(:post).references(:posts).order(Arel.sql(quoted_posts_id))
+ Comment.includes(:post).references(:posts).order(quoted_posts_id)
end
end
@@ -789,7 +789,6 @@ class EagerAssociationTest < ActiveRecord::TestCase
.where("comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'")
.references(:comments)
.scoping do
-
posts = authors(:david).posts.limit(2).to_a
assert_equal 2, posts.size
end
@@ -798,7 +797,6 @@ class EagerAssociationTest < ActiveRecord::TestCase
.where("authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')")
.references(:authors, :comments)
.scoping do
-
count = Post.limit(2).count
assert_equal count, posts.size
end
@@ -970,14 +968,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts(:thinking, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: Arel.sql("UPPER(posts.title)"), limit: 2, offset: 1
+ order: "UPPER(posts.title)", limit: 2, offset: 1
).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: Arel.sql("UPPER(posts.title) DESC"), limit: 2, offset: 1
+ order: "UPPER(posts.title) DESC", limit: 2, offset: 1
).to_a
)
end
@@ -987,14 +985,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts(:thinking, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: [Arel.sql("UPPER(posts.title)"), "posts.id"], limit: 2, offset: 1
+ order: ["UPPER(posts.title)", "posts.id"], limit: 2, offset: 1
).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: [Arel.sql("UPPER(posts.title) DESC"), "posts.id"], limit: 2, offset: 1
+ order: ["UPPER(posts.title) DESC", "posts.id"], limit: 2, offset: 1
).to_a
)
end
@@ -1245,7 +1243,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
Post.all.merge!(select: "posts.*, authors.name as author_name", includes: :comments, joins: :author, order: "posts.id").to_a
end
assert_equal "David", posts[0].author_name
- assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments }
+ assert_equal posts(:welcome).comments.sort_by(&:id), assert_no_queries { posts[0].comments.sort_by(&:id) }
end
def test_eager_loading_with_conditions_on_join_model_preloads
@@ -1257,8 +1255,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_preload_belongs_to_uses_exclusive_scope
- people = Person.males.merge(includes: :primary_contact).to_a
- assert_not_equal people.length, 0
+ people = Person.males.includes(:primary_contact).to_a
+ assert_equal 2, people.length
people.each do |person|
assert_no_queries { assert_not_nil person.primary_contact }
assert_equal Person.find(person.id).primary_contact, person.primary_contact
@@ -1267,16 +1265,17 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_preload_has_many_uses_exclusive_scope
people = Person.males.includes(:agents).to_a
+ assert_equal 2, people.length
people.each do |person|
- assert_equal Person.find(person.id).agents, person.agents
+ assert_equal Person.find(person.id).agents.sort_by(&:id), person.agents.sort_by(&:id)
end
end
def test_preload_has_many_using_primary_key
- expected = Firm.first.clients_using_primary_key.to_a
+ expected = Firm.first.clients_using_primary_key.sort_by(&:id)
firm = Firm.includes(:clients_using_primary_key).first
assert_no_queries do
- assert_equal expected, firm.clients_using_primary_key
+ assert_equal expected, firm.clients_using_primary_key.sort_by(&:id)
end
end
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index 5e6e3e8ec4..604a52655c 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -87,7 +87,6 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
end
private
-
def extend!(model)
ActiveRecord::Associations::Builder::HasMany.send(:define_extensions, model, :association_name) { }
end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 0e9dafeee6..25cfa0a723 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -550,7 +550,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer = project.developers.first
- assert_no_queries do
+ assert_queries(0) do
assert_predicate project.developers, :loaded?
assert_includes project.developers, developer
end
@@ -745,7 +745,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
developer = developers(:david)
developer.projects.reload
- assert_no_queries do
+ assert_queries(0) do
developer.project_ids
developer.project_ids
end
@@ -873,7 +873,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_has_and_belongs_to_many_associations_on_new_records_use_null_relations
projects = Developer.new.projects
- assert_no_queries do
+ assert_queries(0) do
assert_equal [], projects
assert_equal [], projects.where(title: "omg")
assert_equal [], projects.pluck(:title)
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 545bc77fed..6c54c2f1cd 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -2932,8 +2932,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [], reference.ideal_jobs
end
- private
+ def test_has_many_preloading_with_duplicate_records
+ posts = Post.joins(:comments).preload(:comments).to_a
+ assert_equal [1, 2], posts.first.comments.map(&:id)
+ end
+ private
def force_signal37_to_load_all_clients_of_firm
companies(:first_firm).clients_of_firm.load_target
end
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 35da74102d..8d74ae3961 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -626,7 +626,6 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
end
private
-
def assert_includes_and_joins_equal(query, expected, association)
actual = assert_queries(1) { query.joins(association).to_a.uniq }
assert_equal expected, actual
diff --git a/activerecord/test/cases/associations/required_test.rb b/activerecord/test/cases/associations/required_test.rb
index c7a78e6bc4..db7f945a36 100644
--- a/activerecord/test/cases/associations/required_test.rb
+++ b/activerecord/test/cases/associations/required_test.rb
@@ -117,7 +117,6 @@ class RequiredAssociationsTest < ActiveRecord::TestCase
end
private
-
def subclass_of(klass, &block)
subclass = Class.new(klass, &block)
def subclass.name
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 5cbe5d796d..71b5407dcc 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -1087,7 +1087,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
private
-
def new_topic_like_ar_class(&block)
klass = Class.new(ActiveRecord::Base) do
self.table_name = "topics"
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index a6fb9f0af7..d6ac5a1057 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -241,7 +241,7 @@ module ActiveRecord
test "attributes not backed by database columns are always initialized" do
OverloadedType.create!
- model = OverloadedType.first
+ model = OverloadedType.last
assert_nil model.non_existent_decimal
model.non_existent_decimal = "123"
@@ -253,7 +253,7 @@ module ActiveRecord
attribute :non_existent_decimal, :decimal, default: 123
end
child.create!
- model = child.first
+ model = child.last
assert_equal 123, model.non_existent_decimal
end
@@ -264,7 +264,7 @@ module ActiveRecord
attribute :foo, :string, default: "lol"
end
child.create!
- model = child.first
+ model = child.last
assert_equal "lol", model.foo
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 2fb5ca4152..3528ac045f 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -2,6 +2,7 @@
require "cases/helper"
require "models/author"
+require "models/book"
require "models/bird"
require "models/post"
require "models/comment"
@@ -12,12 +13,14 @@ require "models/developer"
require "models/computer"
require "models/invoice"
require "models/line_item"
+require "models/mouse"
require "models/order"
require "models/parrot"
require "models/pirate"
require "models/project"
require "models/ship"
require "models/ship_part"
+require "models/squeak"
require "models/tag"
require "models/tagging"
require "models/treasure"
@@ -39,7 +42,6 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
def self.name; "Person"; end
private
-
def should_be_cool
unless first_name == "cool"
errors.add :first_name, "not cool"
@@ -82,7 +84,6 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
end
private
-
def assert_no_difference_when_adding_callbacks_twice_for(model, association_name)
reflection = model.reflect_on_association(association_name)
assert_no_difference "callbacks_for_model(#{model.name}).length" do
@@ -387,6 +388,20 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
assert_predicate auditlog, :valid?
end
+
+ def test_validation_does_not_validate_non_dirty_association_target
+ mouse = Mouse.create!(name: "Will")
+ Squeak.create!(mouse: mouse)
+
+ mouse.name = nil
+ mouse.save! validate: false
+
+ squeak = Squeak.last
+
+ assert_equal true, squeak.valid?
+ assert_equal true, squeak.mouse.present?
+ assert_equal true, squeak.valid?
+ end
end
class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttributes < ActiveRecord::TestCase
@@ -1673,6 +1688,10 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
super
@pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
@pirate.birds.create(name: "cookoo")
+
+ @author = Author.new(name: "DHH")
+ @author.published_books.build(name: "Rework", isbn: "1234")
+ @author.published_books.build(name: "Remote", isbn: "1234")
end
test "should automatically validate associations" do
@@ -1681,6 +1700,42 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
assert_not_predicate @pirate, :valid?
end
+
+ test "rollbacks whole transaction and raises ActiveRecord::RecordInvalid when associations fail to #save! due to uniqueness validation failure" do
+ author_count_before_save = Author.count
+ book_count_before_save = Book.count
+
+ assert_no_difference "Author.count" do
+ assert_no_difference "Book.count" do
+ exception = assert_raises(ActiveRecord::RecordInvalid) do
+ @author.save!
+ end
+
+ assert_equal("Validation failed: Published books is invalid", exception.message)
+ end
+ end
+
+ assert_equal(author_count_before_save, Author.count)
+ assert_equal(book_count_before_save, Book.count)
+ end
+
+ test "rollbacks whole transaction when associations fail to #save due to uniqueness validation failure" do
+ author_count_before_save = Author.count
+ book_count_before_save = Book.count
+
+ assert_no_difference "Author.count" do
+ assert_no_difference "Book.count" do
+ assert_nothing_raised do
+ result = @author.save
+
+ assert_not(result)
+ end
+ end
+ end
+
+ assert_equal(author_count_before_save, Author.count)
+ assert_equal(book_count_before_save, Book.count)
+ end
end
class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index ddafa468ed..1324bdf9b8 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1141,11 +1141,14 @@ class BasicsTest < ActiveRecord::TestCase
def test_clear_cache!
# preheat cache
c1 = Post.connection.schema_cache.columns("posts")
+ assert_not_equal 0, Post.connection.schema_cache.size
+
ActiveRecord::Base.clear_cache!
+ assert_equal 0, Post.connection.schema_cache.size
+
c2 = Post.connection.schema_cache.columns("posts")
- c1.each_with_index do |v, i|
- assert_not_same v, c2[i]
- end
+ assert_not_equal 0, Post.connection.schema_cache.size
+
assert_equal c1, c2
end
@@ -1412,6 +1415,14 @@ class BasicsTest < ActiveRecord::TestCase
assert_not_includes SymbolIgnoredDeveloper.columns_hash.keys, "first_name"
end
+ test ".columns_hash raises an error if the record has an empty table name" do
+ expected_message = "FirstAbstractClass has no table configured. Set one with FirstAbstractClass.table_name="
+ exception = assert_raises(ActiveRecord::TableNotSpecified) do
+ FirstAbstractClass.columns_hash
+ end
+ assert_equal expected_message, exception.message
+ end
+
test "ignored columns have no attribute methods" do
assert_not_respond_to Developer.new, :first_name
assert_not_respond_to Developer.new, :first_name=
@@ -1496,7 +1507,7 @@ class BasicsTest < ActiveRecord::TestCase
test "creating a record raises if preventing writes" do
error = assert_raises ActiveRecord::ReadOnlyError do
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
Bird.create! name: "Bluejay"
end
end
@@ -1508,7 +1519,7 @@ class BasicsTest < ActiveRecord::TestCase
bird = Bird.create! name: "Bluejay"
error = assert_raises ActiveRecord::ReadOnlyError do
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
bird.update! name: "Robin"
end
end
@@ -1520,7 +1531,7 @@ class BasicsTest < ActiveRecord::TestCase
bird = Bird.create! name: "Bluejay"
error = assert_raises ActiveRecord::ReadOnlyError do
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
bird.destroy!
end
end
@@ -1531,7 +1542,7 @@ class BasicsTest < ActiveRecord::TestCase
test "selecting a record does not raise if preventing writes" do
bird = Bird.create! name: "Bluejay"
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
assert_equal bird, Bird.where(name: "Bluejay").first
end
end
@@ -1539,13 +1550,13 @@ class BasicsTest < ActiveRecord::TestCase
test "an explain query does not raise if preventing writes" do
Bird.create!(name: "Bluejay")
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
assert_queries(2) { Bird.where(name: "Bluejay").explain }
end
end
test "an empty transaction does not raise if preventing writes" do
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
assert_queries(2, ignore_none: true) do
Bird.transaction do
ActiveRecord::Base.connection.materialize_transactions
@@ -1553,4 +1564,59 @@ class BasicsTest < ActiveRecord::TestCase
end
end
end
+
+ test "preventing writes applies to all connections on a handler" do
+ conn1_error = assert_raises ActiveRecord::ReadOnlyError do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
+ assert_equal ActiveRecord::Base.connection, Bird.connection
+ assert_not_equal ARUnit2Model.connection, Bird.connection
+ Bird.create!(name: "Bluejay")
+ end
+ end
+
+ assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn1_error.message
+
+ conn2_error = assert_raises ActiveRecord::ReadOnlyError do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
+ assert_not_equal ActiveRecord::Base.connection, Professor.connection
+ assert_equal ARUnit2Model.connection, Professor.connection
+ Professor.create!(name: "Professor Bluejay")
+ end
+ end
+
+ assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn2_error.message
+ end
+
+ unless in_memory_db?
+ test "preventing writes with multiple handlers" do
+ ActiveRecord::Base.connects_to(database: { writing: :arunit, reading: :arunit })
+
+ conn1_error = assert_raises ActiveRecord::ReadOnlyError do
+ ActiveRecord::Base.connected_to(role: :writing) do
+ assert_equal :writing, ActiveRecord::Base.current_role
+
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
+ Bird.create!(name: "Bluejay")
+ end
+ end
+ end
+
+ assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn1_error.message
+
+ conn2_error = assert_raises ActiveRecord::ReadOnlyError do
+ ActiveRecord::Base.connected_to(role: :reading) do
+ assert_equal :reading, ActiveRecord::Base.current_role
+
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
+ Bird.create!(name: "Bluejay")
+ end
+ end
+ end
+
+ assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn2_error.message
+ ensure
+ ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
+ ActiveRecord::Base.establish_connection(:arunit)
+ end
+ end
end
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index cf6e280898..0d0bf39f79 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -146,7 +146,7 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_quote_batch_order
c = Post.connection
- assert_sql(/ORDER BY #{c.quote_table_name('posts')}\.#{c.quote_column_name('id')}/) do
+ assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("posts.id"))}/i) do
Post.find_in_batches(batch_size: 1) do |batch|
assert_kind_of Array, batch
assert_kind_of Post, batch.first
diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb
index 85685d1d00..720446b39d 100644
--- a/activerecord/test/cases/bind_parameter_test.rb
+++ b/activerecord/test/cases/bind_parameter_test.rb
@@ -93,7 +93,7 @@ if ActiveRecord::Base.connection.prepared_statements
def test_statement_cache_with_in_clause
@connection.clear_cache!
- topics = Topic.where(id: [1, 3])
+ topics = Topic.where(id: [1, 3]).order(:id)
assert_equal [1, 3], topics.map(&:id)
assert_not_includes statement_cache, to_sql_key(topics.arel)
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 33475e2e09..dbd1d03c4c 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -139,6 +139,13 @@ class CalculationsTest < ActiveRecord::TestCase
end
end
+ def test_should_not_use_alias_for_grouped_field
+ assert_sql(/GROUP BY #{Regexp.escape(Account.connection.quote_table_name("accounts.firm_id"))}/i) do
+ c = Account.group(:firm_id).order("accounts_firm_id").sum(:credit_limit)
+ assert_equal [1, 2, 6, 9], c.keys.compact
+ end
+ end
+
def test_should_order_by_grouped_field
c = Account.group(:firm_id).order("firm_id").sum(:credit_limit)
assert_equal [1, 2, 6, 9], c.keys.compact
@@ -185,7 +192,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_limit_is_kept
return if current_adapter?(:OracleAdapter)
- queries = assert_sql { Account.limit(1).count }
+ queries = capture_sql { Account.limit(1).count }
assert_equal 1, queries.length
assert_match(/LIMIT/, queries.first)
end
@@ -193,7 +200,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_offset_is_kept
return if current_adapter?(:OracleAdapter)
- queries = assert_sql { Account.offset(1).count }
+ queries = capture_sql { Account.offset(1).count }
assert_equal 1, queries.length
assert_match(/OFFSET/, queries.first)
end
@@ -201,14 +208,14 @@ class CalculationsTest < ActiveRecord::TestCase
def test_limit_with_offset_is_kept
return if current_adapter?(:OracleAdapter)
- queries = assert_sql { Account.limit(1).offset(1).count }
+ queries = capture_sql { Account.limit(1).offset(1).count }
assert_equal 1, queries.length
assert_match(/LIMIT/, queries.first)
assert_match(/OFFSET/, queries.first)
end
def test_no_limit_no_offset
- queries = assert_sql { Account.count }
+ queries = capture_sql { Account.count }
assert_equal 1, queries.length
assert_no_match(/LIMIT/, queries.first)
assert_no_match(/OFFSET/, queries.first)
@@ -224,15 +231,12 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_apply_distinct_in_count
- queries = assert_sql do
+ queries = capture_sql do
Account.distinct.count
Account.group(:firm_id).distinct.count
end
queries.each do |query|
- # `table_alias_length` in `column_alias_for` would execute
- # "SHOW max_identifier_length" statement in PostgreSQL adapter.
- next if query == "SHOW max_identifier_length"
assert_match %r{\ASELECT(?! DISTINCT) COUNT\(DISTINCT\b}, query
end
end
@@ -464,7 +468,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_not_perform_joined_include_by_default
assert_equal Account.count, Account.includes(:firm).count
- queries = assert_sql { Account.includes(:firm).count }
+ queries = capture_sql { Account.includes(:firm).count }
assert_no_match(/join/i, queries.last)
end
@@ -592,11 +596,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_sum_expression
- if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter)
- assert_equal 636, Account.sum("2 * credit_limit")
- else
- assert_equal 636, Account.sum("2 * credit_limit").to_i
- end
+ assert_equal 636, Account.sum("2 * credit_limit")
end
def test_sum_expression_returns_zero_when_no_records_to_sum
@@ -774,6 +774,12 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [[2, 2], [4, 4]], Reply.includes(:topic).pluck(:id, :"topics.id")
end
+ def test_group_by_with_order_by_virtual_count_attribute
+ expected = { "SpecialPost" => 1, "StiPost" => 2 }
+ actual = Post.group(:type).order(:count).limit(2).maximum(:comments_count)
+ assert_equal expected, actual
+ end if current_adapter?(:PostgreSQLAdapter)
+
def test_group_by_with_limit
expected = { "Post" => 8, "SpecialPost" => 1 }
actual = Post.includes(:comments).group(:type).order(:type).limit(2).count("comments.id")
@@ -840,7 +846,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_pluck_columns_with_same_name
expected = [["The First Topic", "The Second Topic of the day"], ["The Third Topic of the day", "The Fourth Topic of the day"]]
- actual = Topic.joins(:replies)
+ actual = Topic.joins(:replies).order(:id)
.pluck("topics.title", "replies_topics.title")
assert_equal expected, actual
end
diff --git a/activerecord/test/cases/comment_test.rb b/activerecord/test/cases/comment_test.rb
index 584e03d196..25e2f20676 100644
--- a/activerecord/test/cases/comment_test.rb
+++ b/activerecord/test/cases/comment_test.rb
@@ -14,6 +14,9 @@ if ActiveRecord::Base.connection.supports_comments?
class BlankComment < ActiveRecord::Base
end
+ class PkCommented < ActiveRecord::Base
+ end
+
setup do
@connection = ActiveRecord::Base.connection
@@ -35,8 +38,13 @@ if ActiveRecord::Base.connection.supports_comments?
t.index :absent_comment
end
+ @connection.create_table("pk_commenteds", comment: "Table comment", id: false, force: true) do |t|
+ t.integer :id, comment: "Primary key comment", primary_key: true
+ end
+
Commented.reset_column_information
BlankComment.reset_column_information
+ PkCommented.reset_column_information
end
teardown do
@@ -44,6 +52,11 @@ if ActiveRecord::Base.connection.supports_comments?
@connection.drop_table "blank_comments", if_exists: true
end
+ def test_default_primary_key_comment
+ column = Commented.columns_hash["id"]
+ assert_nil column.comment
+ end
+
def test_column_created_in_block
column = Commented.columns_hash["name"]
assert_equal :string, column.type
@@ -164,5 +177,17 @@ if ActiveRecord::Base.connection.supports_comments?
column = Commented.columns_hash["name"]
assert_nil column.comment
end
+
+ def test_comment_on_primary_key
+ column = PkCommented.columns_hash["id"]
+ assert_equal "Primary key comment", column.comment
+ assert_equal "Table comment", @connection.table_comment("pk_commenteds")
+ end
+
+ def test_schema_dump_with_primary_key_comment
+ output = dump_table_schema "pk_commenteds"
+ assert_match %r[create_table "pk_commenteds",.*\s+comment: "Table comment"], output
+ assert_no_match %r[create_table "pk_commenteds",.*\s+comment: "Primary key comment"], output
+ end
end
end
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index 6282759a10..843242a897 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -29,7 +29,7 @@ module ActiveRecord
def test_establish_connection_uses_spec_name
old_config = ActiveRecord::Base.configurations
- config = { "readonly" => { "adapter" => "sqlite3" } }
+ config = { "readonly" => { "adapter" => "sqlite3", "pool" => "5" } }
ActiveRecord::Base.configurations = config
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(ActiveRecord::Base.configurations)
spec = resolver.spec(:readonly)
@@ -367,11 +367,24 @@ module ActiveRecord
assert_same klass2.connection, ActiveRecord::Base.connection
end
+ class ApplicationRecord < ActiveRecord::Base
+ self.abstract_class = true
+ end
+
+ class MyClass < ApplicationRecord
+ end
+
def test_connection_specification_name_should_fallback_to_parent
klassA = Class.new(Base)
klassB = Class.new(klassA)
+ klassC = Class.new(MyClass)
assert_equal klassB.connection_specification_name, klassA.connection_specification_name
+ assert_equal klassC.connection_specification_name, klassA.connection_specification_name
+
+ assert_equal "primary", klassA.connection_specification_name
+ assert_equal "primary", klassC.connection_specification_name
+
klassA.connection_specification_name = "readonly"
assert_equal "readonly", klassB.connection_specification_name
end
diff --git a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
index 515bf5df06..15a045ed23 100644
--- a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
+++ b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
@@ -244,6 +244,25 @@ module ActiveRecord
assert_equal expected, actual
end
+ def test_no_url_sub_key_with_database_url_doesnt_trample_other_envs
+ ENV["DATABASE_URL"] = "postgres://localhost/baz"
+
+ config = { "default_env" => { "database" => "foo" }, "other_env" => { "url" => "postgres://foohost/bardb" } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "database" => "baz",
+ "adapter" => "postgresql",
+ "host" => "localhost"
+ },
+ "other_env" =>
+ { "adapter" => "postgresql",
+ "database" => "bardb",
+ "host" => "foohost"
+ }
+ }
+ assert_equal expected, actual
+ end
+
def test_merge_no_conflicts_with_database_url
ENV["DATABASE_URL"] = "postgres://localhost/foo"
@@ -273,6 +292,61 @@ module ActiveRecord
}
assert_equal expected, actual
end
+
+ def test_merge_no_conflicts_with_database_url_and_adapter
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = { "default_env" => { "adapter" => "postgresql", "pool" => "5" } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost",
+ "pool" => "5"
+ }
+ }
+ assert_equal expected, actual
+ end
+
+ def test_merge_no_conflicts_with_database_url_and_numeric_pool
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = { "default_env" => { "pool" => 5 } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost",
+ "pool" => 5
+ }
+ }
+
+ assert_equal expected, actual
+ end
+
+ def test_tiered_configs_with_database_url
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = {
+ "default_env" => {
+ "primary" => { "pool" => 5 },
+ "animals" => { "pool" => 5 }
+ }
+ }
+
+ expected = {
+ "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost",
+ "pool" => 5
+ }
+
+ ["primary", "animals"].each do |spec_name|
+ configs = ActiveRecord::DatabaseConfigurations.new(config)
+ actual = configs.configs_for(env_name: "default_env", spec_name: spec_name).config
+ assert_equal expected, actual
+ end
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
index 38331aa641..774380d7e0 100644
--- a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
@@ -40,7 +40,7 @@ if current_adapter?(:Mysql2Adapter)
end
def test_enum_type_with_value_matching_other_type
- assert_lookup_type :string, "ENUM('unicode', '8bit', 'none')"
+ assert_lookup_type :string, "ENUM('unicode', '8bit', 'none', 'time')"
end
def test_binary_types
@@ -58,7 +58,6 @@ if current_adapter?(:Mysql2Adapter)
end
private
-
def assert_lookup_type(type, lookup)
cast_type = @connection.send(:type_map).lookup(lookup)
assert_equal type, cast_type.type
diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
index 1c79d776f0..e92bb40632 100644
--- a/activerecord/test/cases/connection_adapters/type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
@@ -109,7 +109,6 @@ unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strin
end
private
-
def assert_lookup_type(type, lookup)
cast_type = @connection.send(:type_map).lookup(lookup)
assert_equal type, cast_type.type
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index a15ad9a45b..ccbb6e16cd 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -507,7 +507,6 @@ module ActiveRecord
pool.schema_cache = schema_cache
pool.with_connection do |conn|
- assert_not_same pool.schema_cache, conn.schema_cache
assert_equal pool.schema_cache.size, conn.schema_cache.size
assert_same pool.schema_cache.columns(:posts), conn.schema_cache.columns(:posts)
end
@@ -695,6 +694,28 @@ module ActiveRecord
end
end
+ def test_public_connections_access_threadsafe
+ _conn1 = @pool.checkout
+ conn2 = @pool.checkout
+
+ connections = @pool.connections
+ found_conn = nil
+
+ # Without assuming too much about implementation
+ # details make sure that a concurrent change to
+ # the pool is thread-safe.
+ connections.each_index do |idx|
+ if connections[idx] == conn2
+ Thread.new do
+ @pool.remove(conn2)
+ end.join
+ end
+ found_conn = connections[idx]
+ end
+
+ assert_not_nil found_conn
+ end
+
private
def with_single_connection_pool
one_conn_spec = ActiveRecord::Base.connection_pool.spec.dup
diff --git a/activerecord/test/cases/database_statements_test.rb b/activerecord/test/cases/database_statements_test.rb
index 1c934602ec..119d48b85e 100644
--- a/activerecord/test/cases/database_statements_test.rb
+++ b/activerecord/test/cases/database_statements_test.rb
@@ -23,7 +23,6 @@ class DatabaseStatementsTest < ActiveRecord::TestCase
end
private
-
def return_the_inserted_id(method:)
# Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
if current_adapter?(:OracleAdapter)
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
index a0e75f4e89..edd2c768d3 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -72,7 +72,6 @@ if ActiveRecord::Base.connection.supports_explain?
end
private
-
def stub_explain_for_query_plans(query_plans = ["query plan foo", "query plan bar"])
explain_called = 0
diff --git a/activerecord/test/cases/finder_respond_to_test.rb b/activerecord/test/cases/finder_respond_to_test.rb
index 66413a98e4..d9e88b3feb 100644
--- a/activerecord/test/cases/finder_respond_to_test.rb
+++ b/activerecord/test/cases/finder_respond_to_test.rb
@@ -54,7 +54,6 @@ class FinderRespondToTest < ActiveRecord::TestCase
end
private
-
def ensure_topic_method_is_not_cached(method_id)
Topic.singleton_class.remove_method method_id if Topic.public_methods.include? method_id
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index ca114d468e..1f2058cc0a 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -245,7 +245,8 @@ class FinderTest < ActiveRecord::TestCase
end
def test_exists_does_not_select_columns_without_alias
- assert_sql(/SELECT\W+1 AS one FROM ["`]topics["`]/i) do
+ c = Topic.connection
+ assert_sql(/SELECT 1 AS one FROM #{Regexp.escape(c.quote_table_name("topics"))}/i) do
Topic.exists?
end
end
@@ -282,6 +283,11 @@ class FinderTest < ActiveRecord::TestCase
assert_not Post.select(:body).distinct.offset(4).exists?
end
+ def test_exists_with_distinct_and_offset_and_eagerload_and_order
+ assert Post.eager_load(:comments).distinct.offset(10).merge(Comment.order(post_id: :asc)).exists?
+ assert_not Post.eager_load(:comments).distinct.offset(11).merge(Comment.order(post_id: :asc)).exists?
+ end
+
# Ensure +exists?+ runs without an error by excluding distinct value.
# See https://github.com/rails/rails/pull/26981.
def test_exists_with_order_and_distinct
@@ -517,6 +523,7 @@ class FinderTest < ActiveRecord::TestCase
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.first
assert_equal expected, Topic.limit(5).first
+ assert_equal expected, Topic.order(nil).first
end
def test_model_class_responds_to_first_bang
@@ -540,6 +547,7 @@ class FinderTest < ActiveRecord::TestCase
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.second
assert_equal expected, Topic.limit(5).second
+ assert_equal expected, Topic.order(nil).second
end
def test_model_class_responds_to_second_bang
@@ -563,6 +571,7 @@ class FinderTest < ActiveRecord::TestCase
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.third
assert_equal expected, Topic.limit(5).third
+ assert_equal expected, Topic.order(nil).third
end
def test_model_class_responds_to_third_bang
@@ -586,6 +595,7 @@ class FinderTest < ActiveRecord::TestCase
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.fourth
assert_equal expected, Topic.limit(5).fourth
+ assert_equal expected, Topic.order(nil).fourth
end
def test_model_class_responds_to_fourth_bang
@@ -609,6 +619,7 @@ class FinderTest < ActiveRecord::TestCase
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.fifth
assert_equal expected, Topic.limit(5).fifth
+ assert_equal expected, Topic.order(nil).fifth
end
def test_model_class_responds_to_fifth_bang
@@ -777,6 +788,7 @@ class FinderTest < ActiveRecord::TestCase
assert_equal expected, clients.first(2)
assert_equal expected, clients.limit(5).first(2)
+ assert_equal expected, clients.order(nil).first(2)
end
def test_implicit_order_column_is_configurable
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 0cb868da6e..a7f01e898e 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -948,7 +948,6 @@ class TransactionalFixturesOnConnectionNotification < ActiveRecord::TestCase
end
private
-
def fire_connection_notification(connection)
assert_called_with(ActiveRecord::Base.connection_handler, :retrieve_connection, ["book"], returns: connection) do
message_bus = ActiveSupport::Notifications.instrumenter
@@ -1367,7 +1366,6 @@ class MultipleDatabaseFixturesTest < ActiveRecord::TestCase
end
private
-
def with_temporary_connection_pool
old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new ActiveRecord::Base.connection_pool.spec
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 543a0aeb39..56c780c4a6 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -189,7 +189,6 @@ end
module InTimeZone
private
-
def in_time_zone(zone)
old_zone = Time.zone
old_tz = ActiveRecord::Base.time_zone_aware_attributes
diff --git a/activerecord/test/cases/hot_compatibility_test.rb b/activerecord/test/cases/hot_compatibility_test.rb
index 7b388ebc5e..f41aea6125 100644
--- a/activerecord/test/cases/hot_compatibility_test.rb
+++ b/activerecord/test/cases/hot_compatibility_test.rb
@@ -115,7 +115,6 @@ class HotCompatibilityTest < ActiveRecord::TestCase
end
private
-
def get_prepared_statement_cache(connection)
connection.instance_variable_get(:@statements)
.instance_variable_get(:@cache)[Process.pid]
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index 629167e9ed..01e4878c3f 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -471,9 +471,9 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_eager_load_belongs_to_primary_key_quoting
- con = Account.connection
+ c = Account.connection
bind_param = Arel::Nodes::BindParam.new(nil)
- assert_sql(/#{con.quote_table_name('companies')}\.#{con.quote_column_name('id')} = (?:#{Regexp.quote(bind_param.to_sql)}|1)/) do
+ assert_sql(/#{Regexp.escape(c.quote_table_name("companies.id"))} = (?:#{Regexp.escape(bind_param.to_sql)}|1)/i) do
Account.all.merge!(includes: :firm).find(1)
end
end
diff --git a/activerecord/test/cases/insert_all_test.rb b/activerecord/test/cases/insert_all_test.rb
index f24c63031c..d086d77081 100644
--- a/activerecord/test/cases/insert_all_test.rb
+++ b/activerecord/test/cases/insert_all_test.rb
@@ -262,7 +262,6 @@ class InsertAllTest < ActiveRecord::TestCase
end
private
-
def capture_log_output
output = StringIO.new
old_logger, ActiveRecord::Base.logger = ActiveRecord::Base.logger, ActiveSupport::Logger.new(output)
diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb
index 82cf281cff..d68e208617 100644
--- a/activerecord/test/cases/json_serialization_test.rb
+++ b/activerecord/test/cases/json_serialization_test.rb
@@ -10,7 +10,6 @@ require "models/comment"
module JsonSerializationHelpers
private
-
def set_include_root_in_json(value)
original_root_in_json = ActiveRecord::Base.include_root_in_json
ActiveRecord::Base.include_root_in_json = value
@@ -24,7 +23,7 @@ class JsonSerializationTest < ActiveRecord::TestCase
include JsonSerializationHelpers
class NamespacedContact < Contact
- column :name, :string
+ column :name, "string"
end
def setup
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 04f9b26960..b468da7c76 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -593,7 +593,6 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
end
private
-
def add_counter_column_to(model, col = "test_count")
model.connection.add_column model.table_name, col, :integer, null: false, default: 0
model.reset_column_information
diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb
index 726ccf925e..ff2a694e66 100644
--- a/activerecord/test/cases/migration/compatibility_test.rb
+++ b/activerecord/test/cases/migration/compatibility_test.rb
@@ -12,6 +12,7 @@ module ActiveRecord
def setup
super
@connection = ActiveRecord::Base.connection
+ @schema_migration = @connection.schema_migration
@verbose_was = ActiveRecord::Migration.verbose
ActiveRecord::Migration.verbose = false
@@ -38,7 +39,7 @@ module ActiveRecord
}.new
assert connection.index_exists?(:testings, :foo, name: "custom_index_name")
- assert_raise(StandardError) { ActiveRecord::Migrator.new(:up, [migration]).migrate }
+ assert_raise(StandardError) { ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate }
assert connection.index_exists?(:testings, :foo, name: "custom_index_name")
end
@@ -53,7 +54,7 @@ module ActiveRecord
}.new
assert connection.index_exists?(:testings, :bar)
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert_not connection.index_exists?(:testings, :bar)
end
@@ -67,7 +68,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert_not connection.index_exists?(:more_testings, :foo_id)
assert_not connection.index_exists?(:more_testings, :bar_id)
@@ -84,7 +85,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:more_testings, :created_at, null: true)
assert connection.column_exists?(:more_testings, :updated_at, null: true)
@@ -101,7 +102,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: true)
assert connection.column_exists?(:testings, :updated_at, null: true)
@@ -117,7 +118,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: true)
assert connection.column_exists?(:testings, :updated_at, null: true)
@@ -131,7 +132,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: true)
assert connection.column_exists?(:testings, :updated_at, null: true)
@@ -146,7 +147,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:more_testings, :created_at, null: false, **precision_implicit_default)
assert connection.column_exists?(:more_testings, :updated_at, null: false, **precision_implicit_default)
@@ -163,7 +164,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default)
assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default)
@@ -179,7 +180,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default)
assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default)
@@ -193,7 +194,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default)
assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default)
@@ -230,7 +231,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :foo, comment: "comment")
end
@@ -243,7 +244,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert_equal "comment", connection.table_comment("testings")
end
@@ -261,7 +262,7 @@ module ActiveRecord
}.new
Testing.create!
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert_equal ["foobar"], Testing.all.map(&:foo)
ensure
ActiveRecord::Base.clear_cache!
diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb
index e0cbb29dcf..0257545330 100644
--- a/activerecord/test/cases/migration/create_join_table_test.rb
+++ b/activerecord/test/cases/migration/create_join_table_test.rb
@@ -151,7 +151,6 @@ module ActiveRecord
end
private
-
def with_table_cleanup
tables_before = connection.data_sources
diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb
index c056199140..da8bdc472a 100644
--- a/activerecord/test/cases/migration/helper.rb
+++ b/activerecord/test/cases/migration/helper.rb
@@ -34,7 +34,6 @@ module ActiveRecord
end
private
-
delegate(*CONNECTION_METHODS, to: :connection)
end
end
diff --git a/activerecord/test/cases/migration/logger_test.rb b/activerecord/test/cases/migration/logger_test.rb
index 28f4cc124b..431047f957 100644
--- a/activerecord/test/cases/migration/logger_test.rb
+++ b/activerecord/test/cases/migration/logger_test.rb
@@ -17,19 +17,20 @@ module ActiveRecord
def setup
super
- ActiveRecord::SchemaMigration.create_table
- ActiveRecord::SchemaMigration.delete_all
+ @schema_migration = ActiveRecord::Base.connection.schema_migration
+ @schema_migration.create_table
+ @schema_migration.delete_all
end
teardown do
- ActiveRecord::SchemaMigration.drop_table
+ @schema_migration.drop_table
end
def test_migration_should_be_run_without_logger
previous_logger = ActiveRecord::Base.logger
ActiveRecord::Base.logger = nil
migrations = [Migration.new("a", 1), Migration.new("b", 2), Migration.new("c", 3)]
- ActiveRecord::Migrator.new(:up, migrations).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration).migrate
ensure
ActiveRecord::Base.logger = previous_logger
end
diff --git a/activerecord/test/cases/migration/references_statements_test.rb b/activerecord/test/cases/migration/references_statements_test.rb
index 769241ba12..451894fc54 100644
--- a/activerecord/test/cases/migration/references_statements_test.rb
+++ b/activerecord/test/cases/migration/references_statements_test.rb
@@ -126,7 +126,6 @@ module ActiveRecord
end
private
-
def with_polymorphic_column
add_column table_name, :supplier_type, :string
add_index table_name, [:supplier_id, :supplier_type]
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 255885b599..20f577b2c5 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -38,6 +38,7 @@ class MigrationTest < ActiveRecord::TestCase
end
Reminder.reset_column_information
@verbose_was, ActiveRecord::Migration.verbose = ActiveRecord::Migration.verbose, false
+ @schema_migration = ActiveRecord::Base.connection.schema_migration
ActiveRecord::Base.connection.schema_cache.clear!
end
@@ -84,7 +85,7 @@ class MigrationTest < ActiveRecord::TestCase
def test_migrator_versions
migrations_path = MIGRATIONS_ROOT + "/valid"
- migrator = ActiveRecord::MigrationContext.new(migrations_path)
+ migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
migrator.up
assert_equal 3, migrator.current_version
@@ -102,23 +103,23 @@ class MigrationTest < ActiveRecord::TestCase
ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
migrations_path = MIGRATIONS_ROOT + "/valid"
- migrator = ActiveRecord::MigrationContext.new(migrations_path)
+ migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
assert_equal true, migrator.needs_migration?
end
def test_any_migrations
- migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid")
+ migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid", @schema_migration)
assert_predicate migrator, :any_migrations?
- migrator_empty = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/empty")
+ migrator_empty = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/empty", @schema_migration)
assert_not_predicate migrator_empty, :any_migrations?
end
def test_migration_version
- migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/version_check")
+ migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/version_check", @schema_migration)
assert_equal 0, migrator.current_version
migrator.up(20131219224947)
assert_equal 20131219224947, migrator.current_version
@@ -190,6 +191,7 @@ class MigrationTest < ActiveRecord::TestCase
assert_not_predicate BigNumber, :table_exists?
GiveMeBigNumbers.up
+ assert_predicate BigNumber, :table_exists?
BigNumber.reset_column_information
assert BigNumber.create(
@@ -248,7 +250,7 @@ class MigrationTest < ActiveRecord::TestCase
assert_not_predicate Reminder, :table_exists?
name_filter = lambda { |migration| migration.name == "ValidPeopleHaveLastNames" }
- migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid")
+ migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid", @schema_migration)
migrator.up(&name_filter)
assert_column Person, :last_name
@@ -310,7 +312,7 @@ class MigrationTest < ActiveRecord::TestCase
end
}.new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
e = assert_raise(StandardError) { migrator.migrate }
@@ -331,7 +333,7 @@ class MigrationTest < ActiveRecord::TestCase
end
}.new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
e = assert_raise(StandardError) { migrator.run }
@@ -354,7 +356,7 @@ class MigrationTest < ActiveRecord::TestCase
end
}.new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 101)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 101)
e = assert_raise(StandardError) { migrator.migrate }
assert_equal "An error has occurred, all later migrations canceled:\n\nSomething broke", e.message
@@ -413,7 +415,7 @@ class MigrationTest < ActiveRecord::TestCase
def test_internal_metadata_stores_environment
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrations_path = MIGRATIONS_ROOT + "/valid"
- migrator = ActiveRecord::MigrationContext.new(migrations_path)
+ migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
migrator.up
assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
@@ -441,7 +443,7 @@ class MigrationTest < ActiveRecord::TestCase
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrations_path = MIGRATIONS_ROOT + "/valid"
- migrator = ActiveRecord::MigrationContext.new(migrations_path)
+ migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
migrator.up
assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
assert_equal "bar", ActiveRecord::InternalMetadata[:foo]
@@ -482,6 +484,7 @@ class MigrationTest < ActiveRecord::TestCase
Thing.reset_table_name
Thing.reset_sequence_name
WeNeedThings.up
+ assert_predicate Thing, :table_exists?
Thing.reset_column_information
assert Thing.create("content" => "hello world")
@@ -502,8 +505,9 @@ class MigrationTest < ActiveRecord::TestCase
ActiveRecord::Base.table_name_suffix = "_suffix"
Reminder.reset_table_name
Reminder.reset_sequence_name
- Reminder.reset_column_information
WeNeedReminders.up
+ assert_predicate Reminder, :table_exists?
+ Reminder.reset_column_information
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
assert_equal "hello world", Reminder.first.content
@@ -636,7 +640,7 @@ class MigrationTest < ActiveRecord::TestCase
if ActiveRecord::Base.connection.supports_advisory_locks?
def test_migrator_generates_valid_lock_id
migration = Class.new(ActiveRecord::Migration::Current).new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
@@ -650,7 +654,7 @@ class MigrationTest < ActiveRecord::TestCase
# It is important we are consistent with how we generate this so that
# exclusive locking works across migrator versions
migration = Class.new(ActiveRecord::Migration::Current).new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
@@ -672,7 +676,7 @@ class MigrationTest < ActiveRecord::TestCase
end
}.new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
with_another_process_holding_lock(lock_id) do
@@ -693,7 +697,7 @@ class MigrationTest < ActiveRecord::TestCase
end
}.new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
with_another_process_holding_lock(lock_id) do
@@ -706,7 +710,7 @@ class MigrationTest < ActiveRecord::TestCase
def test_with_advisory_lock_raises_the_right_error_when_it_fails_to_release_lock
migration = Class.new(ActiveRecord::Migration::Current).new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
e = assert_raises(ActiveRecord::ConcurrentMigrationError) do
@@ -935,7 +939,6 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
end
private
-
def with_bulk_change_table
# Reset columns/indexes cache as we're changing the table
@columns = @indexes = nil
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
index 30e199f1c5..aeba8e1d14 100644
--- a/activerecord/test/cases/migrator_test.rb
+++ b/activerecord/test/cases/migrator_test.rb
@@ -23,8 +23,9 @@ class MigratorTest < ActiveRecord::TestCase
def setup
super
- ActiveRecord::SchemaMigration.create_table
- ActiveRecord::SchemaMigration.delete_all rescue nil
+ @schema_migration = ActiveRecord::Base.connection.schema_migration
+ @schema_migration.create_table
+ @schema_migration.delete_all rescue nil
@verbose_was = ActiveRecord::Migration.verbose
ActiveRecord::Migration.message_count = 0
ActiveRecord::Migration.class_eval do
@@ -36,7 +37,7 @@ class MigratorTest < ActiveRecord::TestCase
end
teardown do
- ActiveRecord::SchemaMigration.delete_all rescue nil
+ @schema_migration.delete_all rescue nil
ActiveRecord::Migration.verbose = @verbose_was
ActiveRecord::Migration.class_eval do
undef :puts
@@ -49,7 +50,7 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrator_with_duplicate_names
e = assert_raises(ActiveRecord::DuplicateMigrationNameError) do
list = [ActiveRecord::Migration.new("Chunky"), ActiveRecord::Migration.new("Chunky")]
- ActiveRecord::Migrator.new(:up, list)
+ ActiveRecord::Migrator.new(:up, list, @schema_migration)
end
assert_match(/Multiple migrations have the name Chunky/, e.message)
end
@@ -57,39 +58,40 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrator_with_duplicate_versions
assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 1)]
- ActiveRecord::Migrator.new(:up, list)
+ ActiveRecord::Migrator.new(:up, list, @schema_migration)
end
end
def test_migrator_with_missing_version_numbers
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
- ActiveRecord::Migrator.new(:up, list, 3).run
+ ActiveRecord::Migrator.new(:up, list, @schema_migration, 3).run
end
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
- ActiveRecord::Migrator.new(:up, list, -1).run
+ ActiveRecord::Migrator.new(:up, list, @schema_migration, -1).run
end
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
- ActiveRecord::Migrator.new(:up, list, 0).run
+ ActiveRecord::Migrator.new(:up, list, @schema_migration, 0).run
end
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
- ActiveRecord::Migrator.new(:up, list, 3).migrate
+ ActiveRecord::Migrator.new(:up, list, @schema_migration, 3).migrate
end
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
- ActiveRecord::Migrator.new(:up, list, -1).migrate
+ ActiveRecord::Migrator.new(:up, list, @schema_migration, -1).migrate
end
end
def test_finds_migrations
- migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid").migrations
+ schema_migration = ActiveRecord::Base.connection.schema_migration
+ migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid", schema_migration).migrations
[[1, "ValidPeopleHaveLastNames"], [2, "WeNeedReminders"], [3, "InnocentJointable"]].each_with_index do |pair, i|
assert_equal migrations[i].version, pair.first
@@ -98,7 +100,8 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_finds_migrations_in_subdirectories
- migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid_with_subdirectories").migrations
+ schema_migration = ActiveRecord::Base.connection.schema_migration
+ migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid_with_subdirectories", schema_migration).migrations
[[1, "ValidPeopleHaveLastNames"], [2, "WeNeedReminders"], [3, "InnocentJointable"]].each_with_index do |pair, i|
assert_equal migrations[i].version, pair.first
@@ -107,8 +110,9 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_finds_migrations_from_two_directories
+ schema_migration = ActiveRecord::Base.connection.schema_migration
directories = [MIGRATIONS_ROOT + "/valid_with_timestamps", MIGRATIONS_ROOT + "/to_copy_with_timestamps"]
- migrations = ActiveRecord::MigrationContext.new(directories).migrations
+ migrations = ActiveRecord::MigrationContext.new(directories, schema_migration).migrations
[[20090101010101, "PeopleHaveHobbies"],
[20090101010202, "PeopleHaveDescriptions"],
@@ -121,14 +125,16 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_finds_migrations_in_numbered_directory
- migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/10_urban").migrations
+ schema_migration = ActiveRecord::Base.connection.schema_migration
+ migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/10_urban", schema_migration).migrations
assert_equal 9, migrations[0].version
assert_equal "AddExpressions", migrations[0].name
end
def test_relative_migrations
+ schema_migration = ActiveRecord::Base.connection.schema_migration
list = Dir.chdir(MIGRATIONS_ROOT) do
- ActiveRecord::MigrationContext.new("valid").migrations
+ ActiveRecord::MigrationContext.new("valid", schema_migration).migrations
end
migration_proxy = list.find { |item|
@@ -138,9 +144,9 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_finds_pending_migrations
- ActiveRecord::SchemaMigration.create!(version: "1")
+ @schema_migration.create!(version: "1")
migration_list = [ActiveRecord::Migration.new("foo", 1), ActiveRecord::Migration.new("bar", 3)]
- migrations = ActiveRecord::Migrator.new(:up, migration_list).pending_migrations
+ migrations = ActiveRecord::Migrator.new(:up, migration_list, @schema_migration).pending_migrations
assert_equal 1, migrations.size
assert_equal migration_list.last, migrations.first
@@ -148,35 +154,38 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrations_status
path = MIGRATIONS_ROOT + "/valid"
+ schema_migration = ActiveRecord::Base.connection.schema_migration
- ActiveRecord::SchemaMigration.create(version: 2)
- ActiveRecord::SchemaMigration.create(version: 10)
+ @schema_migration.create(version: 2)
+ @schema_migration.create(version: 10)
assert_equal [
["down", "001", "Valid people have last names"],
["up", "002", "We need reminders"],
["down", "003", "Innocent jointable"],
["up", "010", "********** NO FILE **********"],
- ], ActiveRecord::MigrationContext.new(path).migrations_status
+ ], ActiveRecord::MigrationContext.new(path, schema_migration).migrations_status
end
def test_migrations_status_in_subdirectories
path = MIGRATIONS_ROOT + "/valid_with_subdirectories"
+ schema_migration = ActiveRecord::Base.connection.schema_migration
- ActiveRecord::SchemaMigration.create(version: 2)
- ActiveRecord::SchemaMigration.create(version: 10)
+ @schema_migration.create(version: 2)
+ @schema_migration.create(version: 10)
assert_equal [
["down", "001", "Valid people have last names"],
["up", "002", "We need reminders"],
["down", "003", "Innocent jointable"],
["up", "010", "********** NO FILE **********"],
- ], ActiveRecord::MigrationContext.new(path).migrations_status
+ ], ActiveRecord::MigrationContext.new(path, schema_migration).migrations_status
end
def test_migrations_status_with_schema_define_in_subdirectories
path = MIGRATIONS_ROOT + "/valid_with_subdirectories"
prev_paths = ActiveRecord::Migrator.migrations_paths
+ schema_migration = ActiveRecord::Base.connection.schema_migration
ActiveRecord::Migrator.migrations_paths = path
ActiveRecord::Schema.define(version: 3) do
@@ -186,16 +195,17 @@ class MigratorTest < ActiveRecord::TestCase
["up", "001", "Valid people have last names"],
["up", "002", "We need reminders"],
["up", "003", "Innocent jointable"],
- ], ActiveRecord::MigrationContext.new(path).migrations_status
+ ], ActiveRecord::MigrationContext.new(path, schema_migration).migrations_status
ensure
ActiveRecord::Migrator.migrations_paths = prev_paths
end
def test_migrations_status_from_two_directories
paths = [MIGRATIONS_ROOT + "/valid_with_timestamps", MIGRATIONS_ROOT + "/to_copy_with_timestamps"]
+ schema_migration = ActiveRecord::Base.connection.schema_migration
- ActiveRecord::SchemaMigration.create(version: "20100101010101")
- ActiveRecord::SchemaMigration.create(version: "20160528010101")
+ @schema_migration.create(version: "20100101010101")
+ @schema_migration.create(version: "20160528010101")
assert_equal [
["down", "20090101010101", "People have hobbies"],
@@ -204,18 +214,18 @@ class MigratorTest < ActiveRecord::TestCase
["down", "20100201010101", "Valid with timestamps we need reminders"],
["down", "20100301010101", "Valid with timestamps innocent jointable"],
["up", "20160528010101", "********** NO FILE **********"],
- ], ActiveRecord::MigrationContext.new(paths).migrations_status
+ ], ActiveRecord::MigrationContext.new(paths, schema_migration).migrations_status
end
def test_migrator_interleaved_migrations
pass_one = [Sensor.new("One", 1)]
- ActiveRecord::Migrator.new(:up, pass_one).migrate
+ ActiveRecord::Migrator.new(:up, pass_one, @schema_migration).migrate
assert pass_one.first.went_up
assert_not pass_one.first.went_down
pass_two = [Sensor.new("One", 1), Sensor.new("Three", 3)]
- ActiveRecord::Migrator.new(:up, pass_two).migrate
+ ActiveRecord::Migrator.new(:up, pass_two, @schema_migration).migrate
assert_not pass_two[0].went_up
assert pass_two[1].went_up
assert pass_two.all? { |x| !x.went_down }
@@ -224,7 +234,7 @@ class MigratorTest < ActiveRecord::TestCase
Sensor.new("Two", 2),
Sensor.new("Three", 3)]
- ActiveRecord::Migrator.new(:down, pass_three).migrate
+ ActiveRecord::Migrator.new(:down, pass_three, @schema_migration).migrate
assert pass_three[0].went_down
assert_not pass_three[1].went_down
assert pass_three[2].went_down
@@ -232,7 +242,7 @@ class MigratorTest < ActiveRecord::TestCase
def test_up_calls_up
migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
- migrator = ActiveRecord::Migrator.new(:up, migrations)
+ migrator = ActiveRecord::Migrator.new(:up, migrations, @schema_migration)
migrator.migrate
assert migrations.all?(&:went_up)
assert migrations.all? { |m| !m.went_down }
@@ -243,7 +253,7 @@ class MigratorTest < ActiveRecord::TestCase
test_up_calls_up
migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
- migrator = ActiveRecord::Migrator.new(:down, migrations)
+ migrator = ActiveRecord::Migrator.new(:down, migrations, @schema_migration)
migrator.migrate
assert migrations.all? { |m| !m.went_up }
assert migrations.all?(&:went_down)
@@ -251,30 +261,31 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_current_version
- ActiveRecord::SchemaMigration.create!(version: "1000")
- migrator = ActiveRecord::MigrationContext.new("db/migrate")
+ @schema_migration.create!(version: "1000")
+ schema_migration = ActiveRecord::Base.connection.schema_migration
+ migrator = ActiveRecord::MigrationContext.new("db/migrate", schema_migration)
assert_equal 1000, migrator.current_version
end
def test_migrator_one_up
calls, migrations = sensors(3)
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1).migrate
assert_equal [[:up, 1]], calls
calls.clear
- ActiveRecord::Migrator.new(:up, migrations, 2).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 2).migrate
assert_equal [[:up, 2]], calls
end
def test_migrator_one_down
calls, migrations = sensors(3)
- ActiveRecord::Migrator.new(:up, migrations).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration).migrate
assert_equal [[:up, 1], [:up, 2], [:up, 3]], calls
calls.clear
- ActiveRecord::Migrator.new(:down, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 1).migrate
assert_equal [[:down, 3], [:down, 2]], calls
end
@@ -282,17 +293,17 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrator_one_up_one_down
calls, migrations = sensors(3)
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1).migrate
assert_equal [[:up, 1]], calls
calls.clear
- ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 0).migrate
assert_equal [[:down, 1]], calls
end
def test_migrator_double_up
calls, migrations = sensors(3)
- migrator = ActiveRecord::Migrator.new(:up, migrations, 1)
+ migrator = ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1)
assert_equal(0, migrator.current_version)
migrator.migrate
@@ -305,7 +316,7 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrator_double_down
calls, migrations = sensors(3)
- migrator = ActiveRecord::Migrator.new(:up, migrations, 1)
+ migrator = ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1)
assert_equal 0, migrator.current_version
@@ -313,7 +324,7 @@ class MigratorTest < ActiveRecord::TestCase
assert_equal [[:up, 1]], calls
calls.clear
- migrator = ActiveRecord::Migrator.new(:down, migrations, 1)
+ migrator = ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 1)
migrator.run
assert_equal [[:down, 1]], calls
calls.clear
@@ -328,12 +339,12 @@ class MigratorTest < ActiveRecord::TestCase
_, migrations = sensors(3)
ActiveRecord::Migration.verbose = true
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1).migrate
assert_not_equal 0, ActiveRecord::Migration.message_count
ActiveRecord::Migration.message_count = 0
- ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 0).migrate
assert_not_equal 0, ActiveRecord::Migration.message_count
end
@@ -341,9 +352,9 @@ class MigratorTest < ActiveRecord::TestCase
_, migrations = sensors(3)
ActiveRecord::Migration.verbose = false
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1).migrate
assert_equal 0, ActiveRecord::Migration.message_count
- ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 0).migrate
assert_equal 0, ActiveRecord::Migration.message_count
end
@@ -351,23 +362,24 @@ class MigratorTest < ActiveRecord::TestCase
calls, migrations = sensors(3)
# migrate up to 1
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1).migrate
assert_equal [[:up, 1]], calls
calls.clear
# migrate down to 0
- ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 0).migrate
assert_equal [[:down, 1]], calls
calls.clear
# migrate down to 0 again
- ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 0).migrate
assert_equal [], calls
end
def test_migrator_going_down_due_to_version_target
+ schema_migration = ActiveRecord::Base.connection.schema_migration
calls, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
migrator.up(1)
assert_equal [[:up, 1]], calls
@@ -382,8 +394,9 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_migrator_output_when_running_multiple_migrations
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
result = migrator.migrate
assert_equal(3, result.count)
@@ -397,8 +410,9 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_migrator_output_when_running_single_migration
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(1)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
result = migrator.run(:up, 1)
@@ -406,8 +420,9 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_migrator_rollback
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
migrator.migrate
assert_equal(3, migrator.current_version)
@@ -426,18 +441,20 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_migrator_db_has_no_schema_migrations_table
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
- ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
- assert_not ActiveRecord::Base.connection.table_exists?("schema_migrations")
+ ActiveRecord::SchemaMigration.drop_table
+ assert_not_predicate ActiveRecord::SchemaMigration, :table_exists?
migrator.migrate(1)
- assert ActiveRecord::Base.connection.table_exists?("schema_migrations")
+ assert_predicate ActiveRecord::SchemaMigration, :table_exists?
end
def test_migrator_forward
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(3)
- migrator = migrator.new("/valid")
+ migrator = migrator.new("/valid", schema_migration)
migrator.migrate(1)
assert_equal(1, migrator.current_version)
@@ -450,18 +467,20 @@ class MigratorTest < ActiveRecord::TestCase
def test_only_loads_pending_migrations
# migrate up to 1
- ActiveRecord::SchemaMigration.create!(version: "1")
+ @schema_migration.create!(version: "1")
+ schema_migration = ActiveRecord::Base.connection.schema_migration
calls, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
migrator.migrate
assert_equal [[:up, 2], [:up, 3]], calls
end
def test_get_all_versions
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
migrator.migrate
assert_equal([1, 2, 3], migrator.get_all_versions)
diff --git a/activerecord/test/cases/multi_db_migrator_test.rb b/activerecord/test/cases/multi_db_migrator_test.rb
new file mode 100644
index 0000000000..650b3af6f0
--- /dev/null
+++ b/activerecord/test/cases/multi_db_migrator_test.rb
@@ -0,0 +1,218 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "cases/migration/helper"
+
+class MultiDbMigratorTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ # Use this class to sense if migrations have gone
+ # up or down.
+ class Sensor < ActiveRecord::Migration::Current
+ attr_reader :went_up, :went_down
+
+ def initialize(name = self.class.name, version = nil)
+ super
+ @went_up = false
+ @went_down = false
+ end
+
+ def up; @went_up = true; end
+ def down; @went_down = true; end
+ end
+
+ def setup
+ super
+ @connection_a = ActiveRecord::Base.connection
+ @connection_b = ARUnit2Model.connection
+
+ @connection_a.schema_migration.create_table
+ @connection_b.schema_migration.create_table
+
+ @connection_a.schema_migration.delete_all rescue nil
+ @connection_b.schema_migration.delete_all rescue nil
+
+ @path_a = MIGRATIONS_ROOT + "/valid"
+ @path_b = MIGRATIONS_ROOT + "/to_copy"
+
+ @schema_migration_a = @connection_a.schema_migration
+ @migrations_a = ActiveRecord::MigrationContext.new(@path_a, @schema_migration_a).migrations
+ @schema_migration_b = @connection_b.schema_migration
+ @migrations_b = ActiveRecord::MigrationContext.new(@path_b, @schema_migration_b).migrations
+
+ @migrations_a_list = [[1, "ValidPeopleHaveLastNames"], [2, "WeNeedReminders"], [3, "InnocentJointable"]]
+ @migrations_b_list = [[1, "PeopleHaveHobbies"], [2, "PeopleHaveDescriptions"]]
+
+ @verbose_was = ActiveRecord::Migration.verbose
+
+ ActiveRecord::Migration.message_count = 0
+ ActiveRecord::Migration.class_eval do
+ undef :puts
+ def puts(*)
+ ActiveRecord::Migration.message_count += 1
+ end
+ end
+ end
+
+ teardown do
+ @connection_a.schema_migration.delete_all rescue nil
+ @connection_b.schema_migration.delete_all rescue nil
+
+ ActiveRecord::Migration.verbose = @verbose_was
+ ActiveRecord::Migration.class_eval do
+ undef :puts
+ def puts(*)
+ super
+ end
+ end
+ end
+
+ def test_finds_migrations
+ @migrations_a_list.each_with_index do |pair, i|
+ assert_equal @migrations_a[i].version, pair.first
+ assert_equal @migrations_a[i].name, pair.last
+ end
+
+ @migrations_b_list.each_with_index do |pair, i|
+ assert_equal @migrations_b[i].version, pair.first
+ assert_equal @migrations_b[i].name, pair.last
+ end
+ end
+
+ def test_migrations_status
+ @schema_migration_a.create(version: 2)
+ @schema_migration_a.create(version: 10)
+
+ assert_equal [
+ ["down", "001", "Valid people have last names"],
+ ["up", "002", "We need reminders"],
+ ["down", "003", "Innocent jointable"],
+ ["up", "010", "********** NO FILE **********"],
+ ], ActiveRecord::MigrationContext.new(@path_a, @schema_migration_a).migrations_status
+
+ @schema_migration_b.create(version: 4)
+
+ assert_equal [
+ ["down", "001", "People have hobbies"],
+ ["down", "002", "People have descriptions"],
+ ["up", "004", "********** NO FILE **********"]
+ ], ActiveRecord::MigrationContext.new(@path_b, @schema_migration_b).migrations_status
+ end
+
+ def test_get_all_versions
+ _, migrator_a = migrator_class(3)
+ migrator_a = migrator_a.new(@path_a, @schema_migration_a)
+
+ migrator_a.migrate
+ assert_equal([1, 2, 3], migrator_a.get_all_versions)
+
+ migrator_a.rollback
+ assert_equal([1, 2], migrator_a.get_all_versions)
+
+ migrator_a.rollback
+ assert_equal([1], migrator_a.get_all_versions)
+
+ migrator_a.rollback
+ assert_equal([], migrator_a.get_all_versions)
+
+ _, migrator_b = migrator_class(2)
+ migrator_b = migrator_b.new(@path_b, @schema_migration_b)
+
+ migrator_b.migrate
+ assert_equal([1, 2], migrator_b.get_all_versions)
+
+ migrator_b.rollback
+ assert_equal([1], migrator_b.get_all_versions)
+
+ migrator_b.rollback
+ assert_equal([], migrator_b.get_all_versions)
+ end
+
+ def test_finds_pending_migrations
+ @schema_migration_a.create!(version: "1")
+ migration_list_a = [ActiveRecord::Migration.new("foo", 1), ActiveRecord::Migration.new("bar", 3)]
+ migrations_a = ActiveRecord::Migrator.new(:up, migration_list_a, @schema_migration_a).pending_migrations
+
+ assert_equal 1, migrations_a.size
+ assert_equal migration_list_a.last, migrations_a.first
+
+ @schema_migration_b.create!(version: "1")
+ migration_list_b = [ActiveRecord::Migration.new("foo", 1), ActiveRecord::Migration.new("bar", 3)]
+ migrations_b = ActiveRecord::Migrator.new(:up, migration_list_b, @schema_migration_b).pending_migrations
+
+ assert_equal 1, migrations_b.size
+ assert_equal migration_list_b.last, migrations_b.first
+ end
+
+ def test_migrator_db_has_no_schema_migrations_table
+ _, migrator = migrator_class(3)
+ migrator = migrator.new(@path_a, @schema_migration_a)
+
+ @schema_migration_a.drop_table
+ assert_not @connection_a.table_exists?("schema_migrations")
+ migrator.migrate(1)
+ assert @connection_a.table_exists?("schema_migrations")
+
+ _, migrator = migrator_class(3)
+ migrator = migrator.new(@path_b, @schema_migration_b)
+
+ @schema_migration_b.drop_table
+ assert_not @connection_b.table_exists?("schema_migrations")
+ migrator.migrate(1)
+ assert @connection_b.table_exists?("schema_migrations")
+ end
+
+ def test_migrator_forward
+ _, migrator = migrator_class(3)
+ migrator = migrator.new(@path_a, @schema_migration_a)
+ migrator.migrate(1)
+ assert_equal(1, migrator.current_version)
+
+ migrator.forward(2)
+ assert_equal(3, migrator.current_version)
+
+ migrator.forward
+ assert_equal(3, migrator.current_version)
+
+ _, migrator_b = migrator_class(3)
+ migrator_b = migrator_b.new(@path_b, @schema_migration_b)
+ migrator_b.migrate(1)
+ assert_equal(1, migrator_b.current_version)
+
+ migrator_b.forward(2)
+ assert_equal(3, migrator_b.current_version)
+
+ migrator_b.forward
+ assert_equal(3, migrator_b.current_version)
+ end
+
+ private
+ def m(name, version)
+ x = Sensor.new name, version
+ x.extend(Module.new {
+ define_method(:up) { yield(:up, x); super() }
+ define_method(:down) { yield(:down, x); super() }
+ }) if block_given?
+ end
+
+ def sensors(count)
+ calls = []
+ migrations = count.times.map { |i|
+ m(nil, i + 1) { |c, migration|
+ calls << [c, migration.version]
+ }
+ }
+ [calls, migrations]
+ end
+
+ def migrator_class(count)
+ calls, migrations = sensors(count)
+
+ migrator = Class.new(ActiveRecord::MigrationContext) {
+ define_method(:migrations) { |*|
+ migrations
+ }
+ }
+ [calls, migrator]
+ end
+end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index bb1c1ea17d..b49e62bee6 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -851,7 +851,6 @@ module NestedAttributesOnACollectionAssociationTests
end
private
-
def association_setter
@association_setter ||= "#{@association_name}_attributes=".to_sym
end
diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb
index 080aeb0989..d783b2945d 100644
--- a/activerecord/test/cases/pooled_connections_test.rb
+++ b/activerecord/test/cases/pooled_connections_test.rb
@@ -72,7 +72,6 @@ class PooledConnectionsTest < ActiveRecord::TestCase
end
private
-
def add_record(name)
ActiveRecord::Base.connection_pool.with_connection { Project.create! name: name }
end
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index eb32b690aa..79bd6906d1 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -335,11 +335,7 @@ class QueryCacheTest < ActiveRecord::TestCase
def test_cache_does_not_wrap_results_in_arrays
Task.cache do
- if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter)
- assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
- else
- assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
- end
+ assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
end
end
@@ -540,8 +536,24 @@ class QueryCacheTest < ActiveRecord::TestCase
ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
end
- private
+ test "query cache is enabled in threads with shared connection" do
+ ActiveRecord::Base.connection_pool.lock_thread = true
+
+ assert_cache :off
+ thread_a = Thread.new do
+ middleware { |env|
+ assert_cache :clean
+ [200, {}, nil]
+ }.call({})
+ end
+
+ thread_a.join
+
+ ActiveRecord::Base.connection_pool.lock_thread = false
+ end
+
+ private
def with_temporary_connection_pool
old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new ActiveRecord::Base.connection_pool.spec
diff --git a/activerecord/test/cases/relation/where_clause_test.rb b/activerecord/test/cases/relation/where_clause_test.rb
index b26a1a1d80..35db3d1175 100644
--- a/activerecord/test/cases/relation/where_clause_test.rb
+++ b/activerecord/test/cases/relation/where_clause_test.rb
@@ -233,7 +233,6 @@ class ActiveRecord::Relation
end
private
-
def table
Arel::Table.new("table")
end
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 3f370e5ede..e74fb1a098 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -363,6 +363,13 @@ module ActiveRecord
assert_match %r{/\*\+ BADHINT \*/}, post_with_hint.to_sql
end
+ def test_does_not_duplicate_optimizer_hints_on_merge
+ escaped_table = Post.connection.quote_table_name("posts")
+ expected = "SELECT /*+ OMGHINT */ #{escaped_table}.* FROM #{escaped_table}"
+ query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
+ assert_equal expected, query
+ end
+
class EnsureRoundTripTypeCasting < ActiveRecord::Type::Value
def type
:string
@@ -405,7 +412,6 @@ module ActiveRecord
end
private
-
def skip_if_sqlite3_version_includes_quoting_bug
if sqlite3_version_includes_quoting_bug?
skip <<-ERROR.squish
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 2417775ef1..1a20fe5dc2 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -298,7 +298,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_reverse_order_with_function
- topics = Topic.order(Arel.sql("length(title)")).reverse_order
+ topics = Topic.order("length(title)").reverse_order
assert_equal topics(:second).title, topics.first.title
end
@@ -308,9 +308,9 @@ class RelationTest < ActiveRecord::TestCase
end
def test_reverse_order_with_function_other_predicates
- topics = Topic.order(Arel.sql("author_name, length(title), id")).reverse_order
+ topics = Topic.order("author_name, length(title), id").reverse_order
assert_equal topics(:second).title, topics.first.title
- topics = Topic.order(Arel.sql("length(author_name), id, length(title)")).reverse_order
+ topics = Topic.order("length(author_name), id, length(title)").reverse_order
assert_equal topics(:fifth).title, topics.first.title
end
@@ -337,21 +337,21 @@ class RelationTest < ActiveRecord::TestCase
def test_reverse_order_with_nulls_first_or_last
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order(Arel.sql("title NULLS FIRST")).reverse_order
+ Topic.order("title NULLS FIRST").reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order(Arel.sql("title NULLS FIRST")).reverse_order
+ Topic.order("title NULLS FIRST").reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order(Arel.sql("title nulls last")).reverse_order
+ Topic.order("title nulls last").reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order(Arel.sql("title NULLS FIRST, author_name")).reverse_order
+ Topic.order("title NULLS FIRST, author_name").reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order(Arel.sql("author_name, title nulls last")).reverse_order
+ Topic.order("author_name, title nulls last").reverse_order
end
- end
+ end if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
def test_default_reverse_order_on_table_without_primary_key
assert_raises(ActiveRecord::IrreversibleOrderError) do
@@ -706,7 +706,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_to_sql_on_eager_join
- expected = assert_sql {
+ expected = capture_sql {
Post.eager_load(:last_comment).order("comments.id DESC").to_a
}.first
actual = Post.eager_load(:last_comment).order("comments.id DESC").to_sql
@@ -1679,7 +1679,7 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.order("comments.body")
assert_equal ["comments"], scope.references_values
- scope = Post.order(Arel.sql("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}"))
+ scope = Post.order("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}")
if current_adapter?(:OracleAdapter)
assert_equal ["COMMENTS"], scope.references_values
else
@@ -1696,7 +1696,7 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.order("comments.body asc")
assert_equal ["comments"], scope.references_values
- scope = Post.order(Arel.sql("foo(comments.body)"))
+ scope = Post.order("foo(comments.body)")
assert_equal [], scope.references_values
end
@@ -1704,7 +1704,7 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.reorder("comments.body")
assert_equal %w(comments), scope.references_values
- scope = Post.reorder(Arel.sql("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}"))
+ scope = Post.reorder("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}")
if current_adapter?(:OracleAdapter)
assert_equal ["COMMENTS"], scope.references_values
else
@@ -1721,7 +1721,7 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.reorder("comments.body asc")
assert_equal %w(comments), scope.references_values
- scope = Post.reorder(Arel.sql("foo(comments.body)"))
+ scope = Post.reorder("foo(comments.body)")
assert_equal [], scope.references_values
end
@@ -1955,8 +1955,8 @@ class RelationTest < ActiveRecord::TestCase
test "joins with order by custom attribute" do
companies = Company.create!([{ name: "test1" }, { name: "test2" }])
companies.each { |company| company.contracts.create! }
- assert_equal companies, Company.joins(:contracts).order(:metadata)
- assert_equal companies.reverse, Company.joins(:contracts).order(metadata: :desc)
+ assert_equal companies, Company.joins(:contracts).order(:metadata, :count)
+ assert_equal companies.reverse, Company.joins(:contracts).order(metadata: :desc, count: :desc)
end
test "delegations do not leak to other classes" do
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 49e9be9565..bb7184c5fc 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -33,6 +33,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
schema_info = ActiveRecord::Base.connection.dump_schema_information
assert_match(/20100201010101.*20100301010101/m, schema_info)
+ assert_includes schema_info, "20100101010101"
ensure
ActiveRecord::SchemaMigration.delete_all
end
diff --git a/activerecord/test/cases/schema_loading_test.rb b/activerecord/test/cases/schema_loading_test.rb
index f539156466..5da2d9e08f 100644
--- a/activerecord/test/cases/schema_loading_test.rb
+++ b/activerecord/test/cases/schema_loading_test.rb
@@ -43,7 +43,6 @@ class SchemaLoadingTest < ActiveRecord::TestCase
end
private
-
def define_model
Class.new(ActiveRecord::Base) do
include SchemaLoadCounter
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index ffe94eee0f..6b6861465b 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -835,7 +835,6 @@ module ActiveRecord
end
private
-
def capture_migration_status
capture(:stdout) do
ActiveRecord::Tasks::DatabaseTasks.migrate_status
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index 552e623fd4..258132835f 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -100,7 +100,6 @@ if current_adapter?(:Mysql2Adapter)
end
private
-
def with_stubbed_connection_establish_connection
ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
@@ -180,7 +179,6 @@ if current_adapter?(:Mysql2Adapter)
end
private
-
def with_stubbed_connection_establish_connection
ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
@@ -233,7 +231,6 @@ if current_adapter?(:Mysql2Adapter)
end
private
-
def with_stubbed_connection_establish_connection
ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index 065ba7734c..f9df650687 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -139,7 +139,6 @@ if current_adapter?(:PostgreSQLAdapter)
end
private
-
def with_stubbed_connection_establish_connection
ActiveRecord::Base.stub(:connection, @connection) do
ActiveRecord::Base.stub(:establish_connection, nil) do
@@ -201,7 +200,6 @@ if current_adapter?(:PostgreSQLAdapter)
end
private
-
def with_stubbed_connection_establish_connection
ActiveRecord::Base.stub(:connection, @connection) do
ActiveRecord::Base.stub(:establish_connection, nil) do
@@ -301,7 +299,6 @@ if current_adapter?(:PostgreSQLAdapter)
end
private
-
def with_stubbed_connection
ActiveRecord::Base.stub(:connection, @connection) do
yield
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index 100bd6a925..19b89ab08c 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -460,7 +460,6 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
private
-
def add_transaction_execution_blocks(record)
record.after_commit_block(:create) { |r| r.history << :commit_on_create }
record.after_commit_block(:update) { |r| r.history << :commit_on_update }
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 6795996cca..b5c1cac3d9 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -1077,7 +1077,6 @@ class TransactionTest < ActiveRecord::TestCase
end
private
-
%w(validation save destroy).each do |filter|
define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do |topic|
meta = class << topic; self; end
diff --git a/activerecord/test/cases/unsafe_raw_sql_test.rb b/activerecord/test/cases/unsafe_raw_sql_test.rb
index d5d8f2a09a..87edb163f2 100644
--- a/activerecord/test/cases/unsafe_raw_sql_test.rb
+++ b/activerecord/test/cases/unsafe_raw_sql_test.rb
@@ -77,7 +77,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
assert_equal ids_expected, ids_disabled
end
- test "order: allows table and column name" do
+ test "order: allows table and column names" do
ids_expected = Post.order(Arel.sql("title")).pluck(:id)
ids_depr = with_unsafe_raw_sql_deprecated { Post.order("posts.title").pluck(:id) }
@@ -87,6 +87,17 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
assert_equal ids_expected, ids_disabled
end
+ test "order: allows quoted table and column names" do
+ ids_expected = Post.order(Arel.sql("title")).pluck(:id)
+
+ quoted_title = Post.connection.quote_table_name("posts.title")
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(quoted_title).pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(quoted_title).pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
test "order: allows column name and direction in string" do
ids_expected = Post.order(Arel.sql("title desc")).pluck(:id)
@@ -116,10 +127,10 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
["asc", "desc", ""].each do |direction|
%w(first last).each do |position|
- ids_expected = Post.order(Arel.sql("type #{direction} nulls #{position}")).pluck(:id)
+ ids_expected = Post.order(Arel.sql("type::text #{direction} nulls #{position}")).pluck(:id)
- ids_depr = with_unsafe_raw_sql_deprecated { Post.order("type #{direction} nulls #{position}").pluck(:id) }
- ids_disabled = with_unsafe_raw_sql_disabled { Post.order("type #{direction} nulls #{position}").pluck(:id) }
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order("type::text #{direction} nulls #{position}").pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order("type::text #{direction} nulls #{position}").pluck(:id) }
assert_equal ids_expected, ids_depr
assert_equal ids_expected, ids_disabled
@@ -130,7 +141,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "order: disallows invalid column name" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.order("len(title) asc").pluck(:id)
+ Post.order("REPLACE(title, 'misc', 'zzzz') asc").pluck(:id)
end
end
end
@@ -146,7 +157,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "order: disallows invalid column with direction" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.order("len(title)" => :asc).pluck(:id)
+ Post.order("REPLACE(title, 'misc', 'zzzz')" => :asc).pluck(:id)
end
end
end
@@ -179,7 +190,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "order: disallows invalid Array arguments" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.order(["author_id", "length(title)"]).pluck(:id)
+ Post.order(["author_id", "REPLACE(title, 'misc', 'zzzz')"]).pluck(:id)
end
end
end
@@ -187,8 +198,8 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "order: allows valid Array arguments" do
ids_expected = Post.order(Arel.sql("author_id, length(title)")).pluck(:id)
- ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", Arel.sql("length(title)")]).pluck(:id) }
- ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", Arel.sql("length(title)")]).pluck(:id) }
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", "length(title)"]).pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", "length(title)"]).pluck(:id) }
assert_equal ids_expected, ids_depr
assert_equal ids_expected, ids_disabled
@@ -197,7 +208,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "order: logs deprecation warning for unrecognized column" do
with_unsafe_raw_sql_deprecated do
assert_deprecated(/Dangerous query method/) do
- Post.order("length(title)")
+ Post.order("REPLACE(title, 'misc', 'zzzz')")
end
end
end
@@ -212,6 +223,16 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
assert_equal titles_expected, titles_disabled
end
+ test "pluck: allows string column name with function and alias" do
+ titles_expected = Post.pluck(Arel.sql("UPPER(title)"))
+
+ titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("UPPER(title) AS title") }
+ titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("UPPER(title) AS title") }
+
+ assert_equal titles_expected, titles_depr
+ assert_equal titles_expected, titles_disabled
+ end
+
test "pluck: allows symbol column name" do
titles_expected = Post.pluck(Arel.sql("title"))
@@ -262,10 +283,21 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
assert_equal titles_expected, titles_disabled
end
+ test "pluck: allows quoted table and column names" do
+ titles_expected = Post.pluck(Arel.sql("title"))
+
+ quoted_title = Post.connection.quote_table_name("posts.title")
+ titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck(quoted_title) }
+ titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck(quoted_title) }
+
+ assert_equal titles_expected, titles_depr
+ assert_equal titles_expected, titles_disabled
+ end
+
test "pluck: disallows invalid column name" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.pluck("length(title)")
+ Post.pluck("REPLACE(title, 'misc', 'zzzz')")
end
end
end
@@ -273,7 +305,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "pluck: disallows invalid column name amongst valid names" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.pluck(:title, "length(title)")
+ Post.pluck(:title, "REPLACE(title, 'misc', 'zzzz')")
end
end
end
@@ -281,7 +313,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "pluck: disallows invalid column names with includes" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.includes(:comments).pluck(:title, "length(title)")
+ Post.includes(:comments).pluck(:title, "REPLACE(title, 'misc', 'zzzz')")
end
end
end
@@ -296,24 +328,25 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "pluck: logs deprecation warning" do
with_unsafe_raw_sql_deprecated do
assert_deprecated(/Dangerous query method/) do
- Post.includes(:comments).pluck(:title, "length(title)")
+ Post.includes(:comments).pluck(:title, "REPLACE(title, 'misc', 'zzzz')")
end
end
end
- def with_unsafe_raw_sql_disabled(&blk)
- with_config(:disabled, &blk)
- end
+ private
+ def with_unsafe_raw_sql_disabled(&block)
+ with_config(:disabled, &block)
+ end
- def with_unsafe_raw_sql_deprecated(&blk)
- with_config(:deprecated, &blk)
- end
+ def with_unsafe_raw_sql_deprecated(&block)
+ with_config(:deprecated, &block)
+ end
- def with_config(new_value, &blk)
- old_value = ActiveRecord::Base.allow_unsafe_raw_sql
- ActiveRecord::Base.allow_unsafe_raw_sql = new_value
- blk.call
- ensure
- ActiveRecord::Base.allow_unsafe_raw_sql = old_value
- end
+ def with_config(new_value, &block)
+ old_value = ActiveRecord::Base.allow_unsafe_raw_sql
+ ActiveRecord::Base.allow_unsafe_raw_sql = new_value
+ yield
+ ensure
+ ActiveRecord::Base.allow_unsafe_raw_sql = old_value
+ end
end
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index 2645776ab7..4dd8a4a82b 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -51,7 +51,7 @@ class I18nValidationTest < ActiveRecord::TestCase
test "validates_uniqueness_of on generated message #{name}" do
Topic.validates_uniqueness_of :title, validation_options
@topic.title = unique_topic.title
- assert_called_with(@topic.errors, :generate_message, [:title, :taken, generate_message_options.merge(value: "unique!")]) do
+ assert_called_with(ActiveModel::Error, :generate_message, [:title, :taken, @topic, generate_message_options.merge(value: "unique!")]) do
@topic.valid?
@topic.errors.messages
end
@@ -61,7 +61,7 @@ class I18nValidationTest < ActiveRecord::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_associated on generated message #{name}" do
Topic.validates_associated :replies, validation_options
- assert_called_with(replied_topic.errors, :generate_message, [:replies, :invalid, generate_message_options.merge(value: replied_topic.replies)]) do
+ assert_called_with(ActiveModel::Error, :generate_message, [:replies, :invalid, replied_topic, generate_message_options.merge(value: replied_topic.replies)]) do
replied_topic.save
replied_topic.errors.messages
end
diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb
index 60ebdce178..7003afa33a 100644
--- a/activerecord/test/cases/yaml_serialization_test.rb
+++ b/activerecord/test/cases/yaml_serialization_test.rb
@@ -130,7 +130,6 @@ class YamlSerializationTest < ActiveRecord::TestCase
end
private
-
def yaml_fixture(file_name)
path = File.expand_path(
"../support/yaml_compatibility_fixtures/#{file_name}.yml",
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index b52b643ad7..da7e4139b1 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -116,6 +116,7 @@ class Author < ActiveRecord::Base
has_many :tags_with_primary_key, through: :posts
has_many :books
+ has_many :published_books, class_name: "PublishedBook"
has_many :unpublished_books, -> { where(status: [:proposed, :written]) }, class_name: "Book"
has_many :subscriptions, through: :books
has_many :subscribers, -> { order("subscribers.nick") }, through: :subscriptions
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
index afdda1a81e..43b82e6047 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -24,3 +24,9 @@ class Book < ActiveRecord::Base
"do publish work..."
end
end
+
+class PublishedBook < ActiveRecord::Base
+ self.table_name = "books"
+
+ validates_uniqueness_of :isbn
+end
diff --git a/activerecord/test/models/club.rb b/activerecord/test/models/club.rb
index bb49fb300c..890e427616 100644
--- a/activerecord/test/models/club.rb
+++ b/activerecord/test/models/club.rb
@@ -15,7 +15,6 @@ class Club < ActiveRecord::Base
accepts_nested_attributes_for :membership
private
-
def private_method
"I'm sorry sir, this is a *private* club, not a *pirate* club"
end
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index a0f48d23f1..339b5c8ca8 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -25,7 +25,6 @@ class Company < AbstractCompany
end
private
-
def private_method
"I am Jack's innermost fears and aspirations"
end
diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb
index 52b7e06a63..320b26b950 100644
--- a/activerecord/test/models/company_in_module.rb
+++ b/activerecord/test/models/company_in_module.rb
@@ -91,7 +91,6 @@ module MyApplication
validate :check_empty_credit_limit
private
-
def check_empty_credit_limit
errors.add("credit_card", :blank) if credit_card.blank?
end
diff --git a/activerecord/test/models/contact.rb b/activerecord/test/models/contact.rb
index 6e02ff199b..d5f6f00691 100644
--- a/activerecord/test/models/contact.rb
+++ b/activerecord/test/models/contact.rb
@@ -10,14 +10,14 @@ module ContactFakeColumns
table_name => "id"
}
- column :id, :integer
- column :name, :string
- column :age, :integer
- column :avatar, :binary
- column :created_at, :datetime
- column :awesome, :boolean
- column :preferences, :string
- column :alternative_id, :integer
+ column :id, "integer"
+ column :name, "string"
+ column :age, "integer"
+ column :avatar, "binary"
+ column :created_at, "datetime"
+ column :awesome, "boolean"
+ column :preferences, "string"
+ column :alternative_id, "integer"
serialize :preferences
@@ -37,7 +37,7 @@ end
class ContactSti < ActiveRecord::Base
extend ContactFakeColumns
- column :type, :string
+ column :type, "string"
def type; "ContactSti" end
end
diff --git a/activerecord/test/models/face.rb b/activerecord/test/models/face.rb
index e900fd40fb..45ccc442ba 100644
--- a/activerecord/test/models/face.rb
+++ b/activerecord/test/models/face.rb
@@ -6,7 +6,7 @@ class Face < ActiveRecord::Base
belongs_to :polymorphic_man, polymorphic: true, inverse_of: :polymorphic_face
# Oracle identifier length is limited to 30 bytes or less, `polymorphic` renamed `poly`
belongs_to :poly_man_without_inverse, polymorphic: true
- # These is a "broken" inverse_of for the purposes of testing
+ # These are "broken" inverse_of associations for the purposes of testing
belongs_to :horrible_man, class_name: "Man", inverse_of: :horrible_face
belongs_to :horrible_polymorphic_man, polymorphic: true, inverse_of: :horrible_polymorphic_face
diff --git a/activerecord/test/models/mouse.rb b/activerecord/test/models/mouse.rb
new file mode 100644
index 0000000000..75a55c125d
--- /dev/null
+++ b/activerecord/test/models/mouse.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class Mouse < ActiveRecord::Base
+ has_many :squeaks, autosave: true
+ validates :name, presence: true
+end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index c3d15a571a..0dfd29e45e 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -101,7 +101,6 @@ class RichPerson < ActiveRecord::Base
before_validation :run_before_validation
private
-
def run_before_create
self.first_name = first_name.to_s + "run_before_create"
end
diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb
index 7973219a79..6bab7a1eb9 100644
--- a/activerecord/test/models/ship.rb
+++ b/activerecord/test/models/ship.rb
@@ -27,7 +27,8 @@ class ShipWithoutNestedAttributes < ActiveRecord::Base
has_many :prisoners, inverse_of: :ship, foreign_key: :ship_id
has_many :parts, class_name: "ShipPart", foreign_key: :ship_id
- validates :name, presence: true
+ validates :name, presence: true, if: -> { true }
+ validates :name, presence: true, if: -> { true }
end
class Prisoner < ActiveRecord::Base
diff --git a/activerecord/test/models/squeak.rb b/activerecord/test/models/squeak.rb
new file mode 100644
index 0000000000..e0a643c238
--- /dev/null
+++ b/activerecord/test/models/squeak.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class Squeak < ActiveRecord::Base
+ belongs_to :mouse
+ accepts_nested_attributes_for :mouse
+end
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 77101090f2..7a864c728c 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -93,7 +93,6 @@ class Topic < ActiveRecord::Base
end
private
-
def default_written_on
self.written_on = Time.now unless attribute_present?("written_on")
end
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index b143035213..911ac808c6 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -62,10 +62,6 @@ ActiveRecord::Schema.define do
t.binary :binary_column, limit: 1
end
- create_table :enum_tests, id: false, force: true do |t|
- t.column :enum_column, "ENUM('text','blob','tiny','medium','long','unsigned','bigint')"
- end
-
execute "DROP PROCEDURE IF EXISTS ten"
execute <<~SQL
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 41920b3719..dd0ff759b6 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -115,7 +115,7 @@ ActiveRecord::Schema.define do
t.column :font_size, :integer, **default_zero
t.column :difficulty, :integer, **default_zero
t.column :cover, :string, default: "hard"
- t.string :isbn
+ t.string :isbn, **case_sensitive_options
t.datetime :published_on
t.index [:author_id, :name], unique: true
t.index :isbn, where: "published_on IS NOT NULL", unique: true
@@ -261,6 +261,7 @@ ActiveRecord::Schema.define do
t.references :developer, index: false
t.references :company, index: false
t.string :metadata
+ t.integer :count
end
create_table :customers, force: true do |t|
@@ -562,6 +563,10 @@ ActiveRecord::Schema.define do
t.string :type
end
+ create_table :mice, force: true do |t|
+ t.string :name
+ end
+
create_table :movies, force: true, id: false do |t|
t.primary_key :movieid
t.string :name
@@ -842,6 +847,10 @@ ActiveRecord::Schema.define do
end
end
+ create_table :squeaks, force: true do |t|
+ t.integer :mouse_id
+ end
+
create_table :prisoners, force: true do |t|
t.belongs_to :ship
end
diff --git a/activerecord/test/support/config.rb b/activerecord/test/support/config.rb
index de0d90a18f..66ae57b382 100644
--- a/activerecord/test/support/config.rb
+++ b/activerecord/test/support/config.rb
@@ -12,7 +12,6 @@ module ARTest
end
private
-
def config_file
Pathname.new(ENV["ARCONFIG"] || TEST_ROOT + "/config.yml")
end