diff options
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/abstract/quoting.rb | 14 | ||||
-rw-r--r-- | activerecord/lib/active_record/database_configurations.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/insert_all.rb | 62 | ||||
-rw-r--r-- | activerecord/test/cases/bind_parameter_test.rb | 2 | ||||
-rw-r--r-- | guides/source/6_0_release_notes.md | 63 | ||||
-rw-r--r-- | railties/CHANGELOG.md | 4 | ||||
-rw-r--r-- | railties/test/isolation/abstract_unit.rb | 2 |
7 files changed, 110 insertions, 39 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index eefe621feb..9d24d839c1 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -138,15 +138,15 @@ module ActiveRecord "'#{quote_string(value.to_s)}'" end - def type_casted_binds(binds) # :nodoc: - if binds.first.is_a?(Array) - binds.map { |column, value| type_cast(value, column) } - else - binds.map { |attr| type_cast(attr.value_for_database) } + private + def type_casted_binds(binds) + if binds.first.is_a?(Array) + binds.map { |column, value| type_cast(value, column) } + else + binds.map { |attr| type_cast(attr.value_for_database) } + end end - end - private def lookup_cast_type(sql_type) type_map.lookup(sql_type) end diff --git a/activerecord/lib/active_record/database_configurations.rb b/activerecord/lib/active_record/database_configurations.rb index a6c702cbbc..4656045fe5 100644 --- a/activerecord/lib/active_record/database_configurations.rb +++ b/activerecord/lib/active_record/database_configurations.rb @@ -106,7 +106,7 @@ module ActiveRecord build_db_config = configs.each_pair.flat_map do |env_name, config| walk_configs(env_name.to_s, "primary", config) - end.compact + end.flatten.compact if url = ENV["DATABASE_URL"] build_url_config(url, build_db_config) diff --git a/activerecord/lib/active_record/insert_all.rb b/activerecord/lib/active_record/insert_all.rb index 3833cb2fcf..98c98d61cd 100644 --- a/activerecord/lib/active_record/insert_all.rb +++ b/activerecord/lib/active_record/insert_all.rb @@ -2,12 +2,14 @@ module ActiveRecord class InsertAll - attr_reader :model, :connection, :inserts, :on_duplicate, :returning, :unique_by + attr_reader :model, :connection, :inserts, :keys + attr_reader :on_duplicate, :returning, :unique_by def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil) raise ArgumentError, "Empty list of attributes passed" if inserts.blank? - @model, @connection, @inserts, @on_duplicate, @returning, @unique_by = model, model.connection, inserts, on_duplicate, returning, unique_by + @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s).to_set + @on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by @returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil? @returning = false if @returning == [] @@ -21,10 +23,6 @@ module ActiveRecord connection.exec_query to_sql, "Bulk Insert" end - def keys - inserts.first.keys.map(&:to_s) - end - def updatable_columns keys - readonly_columns - unique_by_columns end @@ -37,6 +35,17 @@ module ActiveRecord on_duplicate == :update end + def map_key_with_value + inserts.map do |attributes| + attributes = attributes.stringify_keys + verify_attributes(attributes) + + keys.map do |key| + yield key, attributes[key] + end + end + end + private def ensure_valid_options_for_connection! if returning && !connection.supports_insert_returning? @@ -76,6 +85,12 @@ module ActiveRecord Array.wrap(model.primary_key) end + def verify_attributes(attributes) + if keys != attributes.keys.to_set + raise ArgumentError, "All objects being inserted must have the same keys" + end + end + class Builder attr_reader :model @@ -91,29 +106,11 @@ module ActiveRecord end def values_list - columns = connection.schema_cache.columns_hash(model.table_name) - - column_names = columns.keys.to_set - keys = insert_all.keys.to_set - unknown_columns = keys - column_names + types = extract_types_from_columns_on(model.table_name, keys: insert_all.keys) - unless unknown_columns.empty? - raise UnknownAttributeError.new(model.new, unknown_columns.first) - end - - types = keys.map { |key| [ key, connection.lookup_cast_type_from_column(columns[key]) ] }.to_h - - values_list = insert_all.inserts.map do |attributes| - attributes = attributes.stringify_keys - - unless attributes.keys.to_set == keys - raise ArgumentError, "All objects being inserted must have the same keys" - end - - keys.map do |key| - bind = Relation::QueryAttribute.new(key, attributes[key], types[key]) - connection.with_yaml_fallback(bind.value_for_database) - end + values_list = insert_all.map_key_with_value do |key, value| + bind = Relation::QueryAttribute.new(key, value, types[key]) + connection.with_yaml_fallback(bind.value_for_database) end Arel::InsertManager.new.create_values_list(values_list).to_sql @@ -141,6 +138,15 @@ module ActiveRecord quote_columns(insert_all.keys).join(",") end + def extract_types_from_columns_on(table_name, keys:) + columns = connection.schema_cache.columns_hash(table_name) + + unknown_column = (keys - columns.keys).first + raise UnknownAttributeError.new(model.new, unknown_column) if unknown_column + + keys.map { |key| [ key, connection.lookup_cast_type_from_column(columns[key]) ] }.to_h + end + def quote_columns(columns) columns.map(&connection.method(:quote_column_name)) end diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb index eb8b45431f..85685d1d00 100644 --- a/activerecord/test/cases/bind_parameter_test.rb +++ b/activerecord/test/cases/bind_parameter_test.rb @@ -184,7 +184,7 @@ if ActiveRecord::Base.connection.prepared_statements name: "SQL", sql: "select * from topics where id = ?", binds: binds, - type_casted_binds: @connection.type_casted_binds(binds) + type_casted_binds: @connection.send(:type_casted_binds, binds) } event = ActiveSupport::Notifications::Event.new( diff --git a/guides/source/6_0_release_notes.md b/guides/source/6_0_release_notes.md index b6a8536b38..0cf9ca09c7 100644 --- a/guides/source/6_0_release_notes.md +++ b/guides/source/6_0_release_notes.md @@ -77,10 +77,73 @@ Please refer to the [Changelog][railties] for detailed changes. ### Removals +* Remove deprecated `after_bundle` helper inside plugins templates. + ([Commit](https://github.com/rails/rails/commit/4d51efe24e461a2a3ed562787308484cd48370c7)) + +* Remove deprecated support to `config.ru` that uses the application + class as argument of `run`. + ([Commit](https://github.com/rails/rails/commit/553b86fc751c751db504bcbe2d033eb2bb5b6a0b)) + +* Remove deprecated `environment` argument from the rails commands. + ([Commit](https://github.com/rails/rails/commit/e20589c9be09c7272d73492d4b0f7b24e5595571)) + +* Remove deprecated `capify!` method in generators and templates. + ([Commit](https://github.com/rails/rails/commit/9d39f81d512e0d16a27e2e864ea2dd0e8dc41b17)) + +* Remove deprecated `config.secret_token`. + ([Commit](https://github.com/rails/rails/commit/46ac5fe69a20d4539a15929fe48293e1809a26b0)) + ### Deprecations +* Deprecate passing Rack server name as a regular argument to `rails server`. + ([Pull Request](https://github.com/rails/rails/pull/32058)) + +* Deprecate support for using `HOST` environment to specify server IP. + ([Pull Request](https://github.com/rails/rails/pull/32540)) + +* Deprecate accessing hashes returned by `config_for` by non-symbol keys. + ([Pull Request](https://github.com/rails/rails/pull/35198)) + ### Notable changes +* Add an explicit option `--using` or `-u` for specifying the server for the + `rails server` command. + ([Pull Request](https://github.com/rails/rails/pull/32058)) + +* Add ability to see the output of `rails routes` in expanded format. + ([Pull Request](https://github.com/rails/rails/pull/32130)) + +* Run the seed database task using inline Active Job adapter. + ([Pull Request](https://github.com/rails/rails/pull/34953)) + +* Add a command `rails db:system:change` to change the database of the application. + ([Pull Request](https://github.com/rails/rails/pull/34832)) + +* Add `rails test:channels` command to test only Action Cable channels. + ([Pull Request](https://github.com/rails/rails/pull/34947)) + +* Introduce guard against DNS rebinding attacks. + ([Pull Request](https://github.com/rails/rails/pull/33145)) + +* Add ability to abort on failure while running generator commands. + ([Pull Request](https://github.com/rails/rails/pull/34420)) + +* Make Webpacker the default JavaScript compiler for Rails 6. + ([Pull Request](https://github.com/rails/rails/pull/33079)) + +* Add multiple database support for `rails db:migrate:status` command. + ([Pull Request](https://github.com/rails/rails/pull/34137)) + +* Add ability to use different migration paths from multiple databases in + the generators. + ([Pull Request](https://github.com/rails/rails/pull/34021)) + +* Add support for multi environment credentials=. + ([Pull Request](https://github.com/rails/rails/pull/33521)) + +* Make `null_store` as default cache store in test environment. + ([Pull Request](https://github.com/rails/rails/pull/33773)) + Action Cable ------------ diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 264e72bc59..226b949b34 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -79,9 +79,9 @@ *George Claghorn* -* Introduce guard against DNS rebinding attacks +* Introduce guard against DNS rebinding attacks. - The `ActionDispatch::HostAuthorization` is a new middleware that prevent + The `ActionDispatch::HostAuthorization` is a new middleware that prevents against DNS rebinding and other `Host` header attacks. It is included in the development environment by default with the following configuration: diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 14cdf1ab7c..3fcfaa9623 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -123,6 +123,8 @@ module TestHelpers adapter: sqlite3 pool: 5 timeout: 5000 + variables: + statement_timeout: 1000 development: primary: <<: *default |