1. 17 3月, 2019 3 次提交
  2. 06 3月, 2019 1 次提交
    • B
      Add insert_all to ActiveRecord models (#35077) · 91ed21b3
      Bob Lail 提交于
      Adds a method to ActiveRecord allowing records to be inserted in bulk without instantiating ActiveRecord models. This method supports options for handling uniqueness violations by skipping duplicate records or overwriting them in an UPSERT operation.
      
      ActiveRecord already supports bulk-update and bulk-destroy actions that execute SQL UPDATE and DELETE commands directly. It also supports bulk-read actions through `pluck`. It makes sense for it also to support bulk-creation.
      91ed21b3
  3. 25 2月, 2019 1 次提交
  4. 18 1月, 2019 2 次提交
  5. 11 1月, 2019 1 次提交
  6. 30 11月, 2018 1 次提交
    • E
      Add ability to prevent writes to a database · f39d72d5
      Eileen Uchitelle 提交于
      This PR adds the ability to prevent writes to a database even if the
      database user is able to write (ie the database is a primary and not a
      replica).
      
      This is useful for a few reasons: 1) when converting your database from
      a single db to a primary/replica setup - you can fix all the writes on
      reads early on, 2) when we implement automatic database switching or
      when an app is manually switching connections this feature can be used
      to ensure reads are reading and writes are writing. We want to make sure
      we raise if we ever try to write in read mode, regardless of database
      type and 3) for local development if you don't want to set up multiple
      databases but do want to support rw/ro queries.
      
      This should be used in conjunction with `connected_to` in write mode.
      For example:
      
      ```
      ActiveRecord::Base.connected_to(role: :writing) do
        Dog.connection.while_preventing_writes do
          Dog.create! # will raise because we're preventing writes
        end
      end
      
      ActiveRecord::Base.connected_to(role: :reading) do
        Dog.connection.while_preventing_writes do
          Dog.first # will not raise because we're not writing
        end
      end
      ```
      f39d72d5
  7. 03 10月, 2018 1 次提交
  8. 30 9月, 2018 1 次提交
  9. 23 9月, 2018 1 次提交
    • Y
      Enable `Performance/UnfreezeString` cop · 1b86d901
      yuuji.yaginuma 提交于
      In Ruby 2.3 or later, `String#+@` is available and `+@` is faster than `dup`.
      
      ```ruby
      # frozen_string_literal: true
      
      require "bundler/inline"
      
      gemfile(true) do
        source "https://rubygems.org"
      
        gem "benchmark-ips"
      end
      
      Benchmark.ips do |x|
        x.report('+@') { +"" }
        x.report('dup') { "".dup }
        x.compare!
      end
      ```
      
      ```
      $ ruby -v benchmark.rb
      ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
      Warming up --------------------------------------
                        +@   282.289k i/100ms
                       dup   187.638k i/100ms
      Calculating -------------------------------------
                        +@      6.775M (± 3.6%) i/s -     33.875M in   5.006253s
                       dup      3.320M (± 2.2%) i/s -     16.700M in   5.032125s
      
      Comparison:
                        +@:  6775299.3 i/s
                       dup:  3320400.7 i/s - 2.04x  slower
      
      ```
      1b86d901
  10. 14 9月, 2018 1 次提交
    • R
      Fallback to unprepared statement only when bind params limit is exceeded · b571c4f3
      Ryuta Kamizono 提交于
      This is a follow up and/or an alternative of #33844.
      
      Unlike #33844, this would attempt to construct unprepared statement only
      when bind params limit (mysql2 65535, pg 65535, sqlite3 249999) is
      exceeded.
      
      I only defined 65535 as the limit, not defined 249999 for sqlite3, since
      it is an edge case, I'm not excited to add less worth extra code.
      b571c4f3
  11. 09 9月, 2018 1 次提交
  12. 13 8月, 2018 1 次提交
    • E
      Omit BEGIN/COMMIT statements for empty transactions · 0ac81ee6
      Eugene Kenny 提交于
      If a transaction is opened and closed without any queries being run, we
      can safely omit the `BEGIN` and `COMMIT` statements, as they only exist
      to modify the connection's behaviour inside the transaction. This
      removes the overhead of those statements when saving a record with no
      changes, which makes workarounds like `save if changed?` unnecessary.
      
      This implementation buffers transactions inside the transaction manager
      and materializes them the next time the connection is used. For this to
      work, the adapter needs to guard all connection use with a call to
      `materialize_transactions`. Because of this, adapters must opt in to get
      this new behaviour by implementing `supports_lazy_transactions?`.
      
      If `raw_connection` is used to get a reference to the underlying
      database connection, the behaviour is disabled and transactions are
      opened eagerly, as we can't know how the connection will be used.
      However when the connection is checked back into the pool, we can assume
      that the application won't use the reference again and reenable lazy
      transactions. This prevents a single `raw_connection` call from
      disabling lazy transactions for the lifetime of the connection.
      0ac81ee6
  13. 20 4月, 2018 1 次提交
    • Y
      Allow `primary_key` argument to `empty_insert_statement_value` · 947c38da
      Yasuo Honda 提交于
      to support Oracle database support identity data type
      
      Oracle database does not support `INSERT .. DEFAULT VALUES`
      then every insert statement needs at least one column name specified.
      
      When `prefetch_primary_key?` returns `true` insert statement
      always have the primary key name since the primary key value is selected
      from the associated sequence. However, supporting identity data type
      will make `prefetch_primary_key?` returns `false`
      then no primary key column name added.
      As a result, `empty_insert_statement_value` raises `NotImplementedError`
      
      To address this error `empty_insert_statement_value` can take
      one argument `primary_key` to generate insert statement like this.
      
      `INSERT INTO "POSTS" ("ID") VALUES(DEFAULT)`
      
      It needs arity change for the public method but no actual behavior changes for the bundled adapters.
      
      Oracle enhanced adapter `empty_insert_statement_value` implementation will be like this:
      
      ```
      def empty_insert_statement_value(primary_key)
        raise NotImplementedError unless primary_key
        "(#{quote_column_name(primary_key)}) VALUES(DEFAULT)"
      end
      ```
      
      [Raise NotImplementedError when using empty_insert_statement_value with Oracle](https://github.com/rails/rails/pull/28029)
      [Add support for INSERT .. DEFAULT VALUES](https://community.oracle.com/ideas/13845)
      947c38da
  14. 26 1月, 2018 1 次提交
  15. 23 1月, 2018 2 次提交
    • E
      9a5b1fad
    • E
      Build a multi-statement query when inserting fixtures: · 22a26d24
      Edouard CHIN 提交于
      - The `insert_fixtures` method can be optimized by making a single multi statement query for all fixtures having the same connection instead of doing a single query per table
        - The previous code was bulk inserting fixtures for a single table, making X query for X fixture files
        - This patch builds a single **multi statement query** for every tables. Given a set of 3 fixtures (authors, dogs, computers):
        ```ruby
          # before
          %w(authors dogs computers).each do |table|
            sql = build_sql(table)
            connection.query(sql)
          end
      
          # after
      
          sql = build_sql(authors, dogs, computers)
          connection.query(sql)
        ```
      - `insert_fixtures` is now deprecated, `insert_fixtures_set` is the new way to go with performance improvement
      - My tests were done with an app having more than 700 fixtures, the time it takes to insert all of them was around 15s. Using a single multi statement query, it took on average of 8 seconds
      - In order for a multi statement to be executed, mysql needs to be connected with the `MULTI_STATEMENTS` [flag](https://dev.mysql.com/doc/refman/5.7/en/c-api-multiple-queries.html), which is done before inserting the fixtures by reconnecting to da the database with the flag declared. Reconnecting to the database creates some caveats:
        1. We loose all open transactions; Inside the original code, when inserting fixtures, a transaction is open. Multple delete statements are [executed](https://github.com/rails/rails/blob/a681eaf22955734c142609961a6d71746cfa0583/activerecord/lib/active_record/fixtures.rb#L566) and finally the fixtures are inserted. The problem with this patch is that we need to open the transaction only after we reconnect to the DB otherwise reconnecting drops the open transaction which doesn't commit all delete statements and inserting fixtures doesn't work since we duplicated them (Primary key duplicate exception)...
          - In order to fix this problem, the transaction is now open directly inside the `insert_fixtures` method, right after we reconnect to the db
          - As an effect, since the transaction is open inside the `insert_fixtures` method, the DELETE statements need to be executed here since the transaction is open later
        2. The same problem happens for the `disable_referential_integrity` since we reconnect, the `FOREIGN_KEY_CHECKS` is reset to the original value
          - Same solution as 1. , the disable_referential_integrity can be called after we reconnect to the transaction
        3. When the multi statement query is executed, no other queries can be performed until we paginate over the set of results, otherwise mysql throws a "Commands out of sync" [Ref](https://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html)
          - Iterating over the set of results until `mysql_client.next_result` is false. [Ref](https://github.com/brianmario/mysql2#multiple-result-sets)
      - Removed the `active_record.sql "Fixture delete"` notification, the delete statements are now inside the INSERT's one
      - On mysql the `max_allowed_packet` is looked up:
        1. Before executing the multi-statements query, we check the packet length of each statements, if the packet is bigger than the max_allowed_packet config, an `ActiveRecordError` is raised
        2. Otherwise we concatenate the current sql statement into the previous and so on until the packet is `< max_allowed_packet`
      22a26d24
  16. 22 8月, 2017 1 次提交
  17. 18 8月, 2017 2 次提交
  18. 04 8月, 2017 1 次提交
  19. 30 7月, 2017 1 次提交
  20. 25 7月, 2017 1 次提交
    • S
      Fix test failures when prepared statements are disabled · 846832ae
      Sean Griffin 提交于
      This also reverts the change to enable prepared statements by default on
      MySQL (though I suspect we could enable them and it'd be great). This
      change brings back a collector closer to the old `Bind` collector in
      Arel. However, this one lives in AR, since this is an AR specific need.
      Additionally, we only use it for statement caching, since the new
      substitute collector in Arel is higher performance for most cases.
      846832ae
  21. 24 7月, 2017 1 次提交
    • S
      Refactor Active Record to let Arel manage bind params · 213796fb
      Sean Griffin 提交于
      A common source of bugs and code bloat within Active Record has been the
      need for us to maintain the list of bind values separately from the AST
      they're associated with. This makes any sort of AST manipulation
      incredibly difficult, as any time we want to potentially insert or
      remove an AST node, we need to traverse the entire tree to find where
      the associated bind parameters are.
      
      With this change, the bind parameters now live on the AST directly.
      Active Record does not need to know or care about them until the final
      AST traversal for SQL construction. Rather than returning just the SQL,
      the Arel collector will now return both the SQL and the bind parameters.
      At this point the connection adapter will have all the values that it
      had before.
      
      A bit of this code is janky and something I'd like to refactor later. In
      particular, I don't like how we're handling associations in the
      predicate builder, the special casing of `StatementCache::Substitute` in
      `QueryAttribute`, or generally how we're handling bind value replacement
      in the statement cache when prepared statements are disabled.
      
      This also mostly reverts #26378, as it moved all the code into a
      location that I wanted to delete.
      
      /cc @metaskills @yahonda, this change will affect the adapters
      
      Fixes #29766.
      Fixes #29804.
      Fixes #26541.
      Close #28539.
      Close #24769.
      Close #26468.
      Close #26202.
      
      There are probably other issues/PRs that can be closed because of this
      commit, but that's all I could find on the first few pages.
      213796fb
  22. 20 7月, 2017 1 次提交
  23. 02 7月, 2017 1 次提交
  24. 01 7月, 2017 1 次提交
  25. 30 6月, 2017 1 次提交
    • R
      Don't cache queries for schema statements · 21d040f9
      Ryuta Kamizono 提交于
      `test_middleware_caches` is sometimes failed since #29454.
      The failure is due to schema statements are affected by query caching.
      Bypassing query caching for schema statements to avoid the issue.
      21d040f9
  26. 21 6月, 2017 2 次提交
    • K
      Avoid begin/rescue in fixture quoting · fa14332a
      Kir Shatrov 提交于
      Scalar values like arrays and hashes can't be inserted
      directly into table. Previously, the way to determine if
      the value is scalar was to try quoting it. If `quote` raised
      with an error than the value has to be converted to YAML.
      
      This flow is not very obvious. Ideally we could have
      a `quotable?` method in the connection, but I think
      that we can avoid begin/rescue block by simply checking
      if the value is Array or Hash.
      
      https://github.com/rails/rails/commit/aa31d21f5f4fc4d679e74a60f9df9706da7de373
      fa14332a
    • K
      Use bulk INSERT to insert fixtures · 4ee42379
      Kir Shatrov 提交于
      Improves the performance from O(n) to O(1).
      Previously it would require 50 queries to
      insert 50 fixtures. Now it takes only one query.
      
      Disabled on sqlite which doesn't support multiple inserts.
      4ee42379
  27. 20 6月, 2017 1 次提交
  28. 01 5月, 2017 1 次提交
  29. 04 1月, 2017 1 次提交
  30. 30 12月, 2016 2 次提交
  31. 29 12月, 2016 1 次提交
  32. 24 12月, 2016 1 次提交
  33. 16 12月, 2016 1 次提交
    • R
      fix QueryCache nil dup · e220fda3
      Richard Monette 提交于
      make sql statements frozen
      
      dup if arel is not our string
      
      expect runtime error
      
      dont wrap runtime error in invalid
      
      log errors will now be treated as runtime errors
      
      update changelog
      e220fda3