1. 17 7月, 2020 1 次提交
  2. 14 7月, 2020 1 次提交
    • R
      Allow attribute's default to be configured but keeping its own type · 8c793aa8
      Ryuta Kamizono 提交于
      ```ruby
      class Post < ActiveRecord::Base
        attribute :written_at, default: -> { Time.now.utc }
      end
      
      # Rails 6.0
      Post.type_for_attribute(:written_at) # => #<Type::Value ... precision: nil, ...>
      
      # Rails 6.1
      Post.type_for_attribute(:written_at) # => #<Type::DateTime ... precision: 6, ...>
      ```
      
      This is an alternative of #39797.
      
      Context https://github.com/rails/rails/pull/39797#issuecomment-655191817.
      
      If people intend to override the existing type on the attribute, usually
      an overriding type is explicitly specified, so I agree that the current
      behavior (drop existing type information if type is omitted) is
      practically quite useless, and it is almost like a bug in that there is
      no way to override just the default.
      
      So I'd like to change the current behavior on existing attributes
      without a deprecation as a fix.
      
      Closes #39797.
      
      See also https://github.com/kufu/activerecord-bitemporal/pull/57.
      8c793aa8
  3. 10 7月, 2020 1 次提交
  4. 07 7月, 2020 1 次提交
  5. 06 7月, 2020 1 次提交
    • R
      Support `where` with comparison operators (`>`, `>=`, `<`, and `<=`) · 6d6ec6f9
      Ryuta Kamizono 提交于
      ```ruby
      posts = Post.order(:id)
      
      posts.where("id >": 9).pluck(:id)  # => [10, 11]
      posts.where("id >=": 9).pluck(:id) # => [9, 10, 11]
      posts.where("id <": 3).pluck(:id)  # => [1, 2]
      posts.where("id <=": 3).pluck(:id) # => [1, 2, 3]
      ```
      
      From type casting and table/column name resolution's point of view,
      `where("create_at >=": time)` is better alternative than `where("create_at >= ?", time)`.
      
      ```ruby
      class Post < ActiveRecord::Base
        attribute :created_at, :datetime, precision: 3
      end
      
      time = Time.now.utc # => 2020-06-24 10:11:12.123456 UTC
      
      Post.create!(created_at: time) # => #<Post id: 1, created_at: "2020-06-24 10:11:12.123000">
      
      # SELECT `posts`.* FROM `posts` WHERE (created_at >= '2020-06-24 10:11:12.123456')
      Post.where("created_at >= ?", time) # => []
      
      # SELECT `posts`.* FROM `posts` WHERE `posts`.`created_at` >= '2020-06-24 10:11:12.123000'
      Post.where("created_at >=": time) # => [#<Post id: 1, created_at: "2020-06-24 10:11:12.123000">]
      ```
      6d6ec6f9
  6. 15 6月, 2020 1 次提交
    • R
      Deprecate YAML loading from legacy format older than Rails 5.0 · cccf6c31
      Ryuta Kamizono 提交于
      `LegacyYamlAdapter` was added at afc124c3 (and 4e721702), to be able to
      load the previous version formatted YAML.
      
      Now, that has became quite useless, since we sometimes removed/added
      dedicated type classes which are referred in the legacy format (e.g.
      `MysqlDateTime`, `MysqlString`, `SQLite3Integer`), so it is unlikely
      that is able to load it correctly any longer.
      cccf6c31
  7. 11 6月, 2020 1 次提交
    • S
      Add a setting to specify that all string columns should be immutable · 332c3364
      Sean Griffin 提交于
      In Rails 4.2 we introduced mutation detection, to remove the need to
      call `attribute_will_change!` after modifying a field. One side effect
      of that change was that we needed to enforce that the
      `_before_type_cast` form of a value is a different object than the post
      type cast value, if the value is mutable. That contract is really only
      relevant for strings, but it meant we needed to dup them.
      
      In Rails 5 we added the `ImmutableString` type, to allow people to opt
      out of this duping in places where the memory usage was causing
      problems, and they don't need to mutate that field.
      
      This takes that a step further, and adds a class-level setting to
      specify whether strings should be frozen by default or not. The default
      value of this setting is `false` (strings are mutable), and I do not
      plan on changing that.
      
      While I think that immutable strings by default would be a reasonable
      default for new applications, I do not think that we would be able to
      document the value of this setting in a place that people will be
      looking when they can't figure out why some string is frozen.
      Realistically, the number of applications where this setting is relevant
      is fairly small, so I don't think it would make sense to ever enable it
      by default.
      332c3364
  8. 10 6月, 2020 1 次提交
  9. 07 6月, 2020 3 次提交
    • R
      Fixup CHANGELOGs [ci skip] · cfb7c16a
      Ryuta Kamizono 提交于
      cfb7c16a
    • R
      Support `relation.and` for intersection as Set theory · 7219eb2c
      Ryuta Kamizono 提交于
      As described at #39328, `relation.merge` behaves inspired as Hash-like
      merge for where clause. In other words, currently there is no official
      way to intersect the result by both relation conditions (i.e. there is
      no official way to maintain both relation conditions).
      
      To resolve that issue, I'd like to support a way to intersect relations
      as `relation.and`.
      
      ```ruby
      david_and_mary = Author.where(id: [david, mary])
      mary_and_bob   = Author.where(id: [mary, bob]) # => [bob]
      
      david_and_mary.merge(mary_and_bob) # => [mary, bob]
      
      david_and_mary.and(mary_and_bob) # => [mary]
      david_and_mary.or(mary_and_bob)  # => [david, mary, bob]
      ```
      
      Fixes #39232.
      7219eb2c
    • R
      Deprecate inconsistent behavior that merging conditions on the same column · 930bfb93
      Ryuta Kamizono 提交于
      Related to #39250 and #39236.
      
      The purpose of the change here is to unify inconsistent behavior on the
      merging.
      
      For now, mergee side condition is replaced by merger side condition only
      if both arel nodes are Equality or In clauses.
      
      In other words, if mergee side condition is not Equality or In clauses
      (e.g. `between`, `or`, `gt`, `lt`, etc), those conditions will be kept
      even on the same column.
      
      This behavior is harder to predict unless people are familiar with the
      merging behavior.
      
      Originally I suppose this behavior is just an implementation issue
      rather than an intended one, since `unscope` and `rewhere`, which were
      introduced later than `merge`, works more consistently.
      
      Since most of the conditions are usually Equality and In clauses, I
      don't suppose most people have encountered this merging issue, but I'd
      like to deprecate the inconsistent behavior and will completely unify
      that to improve a future UX.
      
      ```ruby
      # Rails 6.1 (IN clause is replaced by merger side equality condition)
      Author.where(id: [david.id, mary.id]).merge(Author.where(id: bob)) # => [bob]
      
      # Rails 6.1 (both conflict conditions exists, deprecated)
      Author.where(id: david.id..mary.id).merge(Author.where(id: bob)) # => []
      
      # Rails 6.1 with rewhere to migrate to Rails 6.2's behavior
      Author.where(id: david.id..mary.id).merge(Author.where(id: bob), rewhere: true) # => [bob]
      
      # Rails 6.2 (same behavior with IN clause, mergee side condition is consistently replaced)
      Author.where(id: [david.id, mary.id]).merge(Author.where(id: bob)) # => [bob]
      Author.where(id: david.id..mary.id).merge(Author.where(id: bob)) # => [bob]
      ```
      930bfb93
  10. 04 6月, 2020 1 次提交
  11. 03 6月, 2020 2 次提交
    • A
      Fix issue with expression index in insert_all · 3315cc76
      Austen Madden 提交于
      Based on the docs which state unique_by option of insert_all can use the
      index name if desired, one would expect the method to work normally and
      use the `unique_by` option to determine duplicates.
      
      However, there's an issue where the insert_all expects a Set instead
      of the string representing the index expression it is given. This causes
      an error. Returning the string expression instead of attempting to
      format it works perfectly though.
      3315cc76
    • F
      1944a7e7
  12. 01 6月, 2020 1 次提交
  13. 31 5月, 2020 1 次提交
    • R
      Deprecate passing an Active Record object to `quote`/`type_cast` directly · 87886c93
      Ryuta Kamizono 提交于
      Follow up to #27962.
      
      #27962 only deprecated `quoted_id`, but still conservatively allowed
      passing an Active Record object.
      
      Since the quoting methods on a `connection` are low-level API and
      querying API does not rely on that ability, so people should pass casted
      value instead of an Active Record object if using the quoting methods
      directly.
      87886c93
  14. 28 5月, 2020 1 次提交
  15. 24 5月, 2020 3 次提交
    • R
      Default engine `ENGINE=InnoDB` is no longer dumped to make schema more agnostic · 3c6be5e5
      Ryuta Kamizono 提交于
      5 years ago, I made dumping full table options at #17569, especially to
      dump `ENGINE=InnoDB ROW_FORMAT=DYNAMIC` to use utf8mb4 with large key
      prefix.
      
      In that time, omitting the default engine `ENGINE=InnoDB` was not useful
      since `ROW_FORMAT=DYNAMIC` always remains as long as using utf8mb4 with
      large key prefix.
      
      But now, MySQL 5.7.9 has finally changed the default row format to
      DYNAMIC, utf8mb4 with large key prefix can be used without dumping the
      default engine and the row format explicitly.
      
      So now is a good time to make the default engine is omitted.
      
      Before:
      
      ```ruby
      create_table "accounts", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
      end
      ```
      
      After:
      
      ```ruby
      create_table "accounts", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
      end
      ```
      
      To entirely omit `:options` option to make schema agnostic, I've added
      `:charset` and `:collation` table options to exclude `CHARSET` and
      `COLLATE` from `:options`.
      
      Fixes #26209.
      Closes #29472.
      
      See also #33608, #33853, and #34742.
      3c6be5e5
    • D
      fd8fd4ae
    • R
      Deprecate aggregations with group by duplicated fields · 2580b83f
      Ryuta Kamizono 提交于
      We've learned that `merge` causes duplicated multiple values easily, so
      if we missed to deduplicate the values, it will cause weird behavior
      like #38052, #39171.
      
      I've investigated the deduplication for the values, at least that had
      existed since Rails 3.0.
      
      https://github.com/rails/rails/commit/bed9179aa1496f6d28891cf515af0d7e515ebbab
      
      Aggregations with group by multiple fields was introduced at Rails 3.1,
      but we missed the deduplication for the aggregation result, unlike the
      generated SQL.
      
      https://github.com/rails/rails/commit/a5cdf0b9eb860c4370ae5fde231e1b61f71b6b65
      
      While the investigation, I've found that `annotate` is also missed the
      deduplication.
      
      I don't suppose this weird behavior is intended for both.
      
      So I'd like to deprecate the duplicated behavior in Rails 6.1, and will
      be deduplicated all multiple values in Rails 6.2.
      
      To migrate to Rails 6.2's behavior, use `uniq!(:group)` to deduplicate
      group fields.
      
      ```ruby
      accounts = Account.group(:firm_id)
      
      # duplicated group fields, deprecated.
      accounts.merge(accounts.where.not(credit_limit: nil)).sum(:credit_limit)
      # => {
      #   [1, 1] => 50,
      #   [2, 2] => 60
      # }
      
      # use `uniq!(:group)` to deduplicate group fields.
      accounts.merge(accounts.where.not(credit_limit: nil)).uniq!(:group).sum(:credit_limit)
      # => {
      #   1 => 50,
      #   2 => 60
      # }
      ```
      2580b83f
  16. 20 5月, 2020 1 次提交
    • A
      Resolve conflict between counter cache and optimistic locking · cb554457
      Aaron Lipman 提交于
      Bump an Active Record instance's lock version after updating its counter
      cache. This avoids raising an unnecessary ActiveRecord::StaleObjectError
      upon subsequent transactions by maintaining parity with the
      corresponding database record's lock_version column.
      cb554457
  17. 18 5月, 2020 3 次提交
    • R
      Support merging option `:rewhere` to allow mergee side condition to be replaced exactly · f6fb3271
      Ryuta Kamizono 提交于
      Related #39236.
      
      `relation.merge` method sometimes replaces mergee side condition, but
      sometimes maintain both conditions unless `relation.rewhere` is used.
      
      It is very hard to predict merging result whether mergee side condition
      will be replaced or not.
      
      One existing way is to use `relation.rewhere` for merger side relation,
      but it is also hard to predict a relation will be used for `merge` in
      advance, except one-time relation for `merge`.
      
      To address that issue, I propose to support merging option `:rewhere`,
      to allow mergee side condition to be replaced exactly.
      
      That option will allow non-`rewhere` relation behaves as `rewhere`d
      relation.
      
      ```ruby
      david_and_mary = Author.where(id: david.id..mary.id)
      
      # both conflict conditions exists
      david_and_mary.merge(Author.where(id: bob)) # => []
      
      # mergee side condition is replaced by rewhere
      david_and_mary.merge(Author.rewhere(id: bob)) # => [bob]
      
      # mergee side condition is replaced by rewhere option
      david_and_mary.merge(Author.where(id: bob), rewhere: true) # => [bob]
      ```
      f6fb3271
    • R
      trailing spaces [ci skip] · a2a861d3
      Ryuta Kamizono 提交于
      a2a861d3
    • D
      Add signed ids to Active Record (#39313) · 1a3dc42c
      David Heinemeier Hansson 提交于
      Add support for finding records based on signed ids, which are tamper-proof, verified ids that can be set to expire and scoped with a purpose. This is particularly useful for things like password reset or email verification, where you want the bearer of the signed id to be able to interact with the underlying record, but usually only within a certain time period.
      1a3dc42c
  18. 13 5月, 2020 1 次提交
    • R
      Fix `minimum` and `maximum` on time zone aware attributes · 11751b2e
      Ryuta Kamizono 提交于
      This is the opposite direction of #39039.
      
      #39111 fixes `minimum` and `maximum` on date columns with type casting
      by column type on the database. But column type has no information for
      time zone aware attributes, it means that attribute type should always
      be precedence over column type. I've realized that fact in the related
      issue report #39248.
      
      I've reverted the expectation of #39039, to make time zone aware
      attributes works.
      11751b2e
  19. 10 5月, 2020 2 次提交
  20. 07 5月, 2020 1 次提交
    • R
      Allow `unscope` to be aware of table name qualified values · 5136277f
      Ryuta Kamizono 提交于
      It is possible to unscope only the column in the specified table.
      
      ```ruby
      posts = Post.joins(:comments).group(:"posts.hidden")
      posts = posts.where("posts.hidden": false, "comments.hidden": false)
      
      posts.count
      # => { false => 10 }
      
      # unscope both hidden columns
      posts.unscope(where: :hidden).count
      # => { false => 11, true => 1 }
      
      # unscope only comments.hidden column
      posts.unscope(where: :"comments.hidden").count
      # => { false => 11 }
      ```
      Co-authored-by: NSlava Korolev <korolvs@gmail.com>
      5136277f
  21. 05 5月, 2020 2 次提交
    • R
      Fix `rewhere` to truly overwrite collided where clause by new where clause · 6187b713
      Ryuta Kamizono 提交于
      ```ruby
      steve = Person.find_by(name: "Steve")
      david = Author.find_by(name: "David")
      
      relation = Essay.where(writer: steve)
      
      # Before
      relation.rewhere(writer: david).to_a # => []
      
      # After
      relation.rewhere(writer: david).to_a # => [david]
      ```
      
      For now `rewhere` only works for truly column names, doesn't work for
      alias attributes, nested conditions, associations.
      
      To fix that, need to build new where clause first, and then get
      attribute names from new where clause.
      6187b713
    • A
      Inspect time attributes with subsec · a3f4202c
      akinomaeni 提交于
      before
      ```
      p Knot.create
      => #<Knot id: 1, created_at: "2016-05-05 01:29:47">
      ```
      
      after
      ```
      p Knot.create
      => #<Knot id: 1, created_at: "2016-05-05 01:29:47.116928000">
      ```
      a3f4202c
  22. 02 5月, 2020 1 次提交
  23. 30 4月, 2020 1 次提交
    • R
      Deprecate `allowed_index_name_length` in `DatabaseLimits` · ab2d859e
      Ryuta Kamizono 提交于
      `allowed_index_name_length` was used for internal temporary operations
      in SQLite3, since index name in SQLite3 must be globally unique and
      SQLite3 doesn't have ALTER TABLE feature (so it is emulated by creating
      temporary table with prefix).
      
      `allowed_index_name_length` was to reserve the margin for the prefix,
      but actually SQLite3 doesn't have a limitation for identifier name
      length, so the margin has removed at 36901e6e.
      
      Now `allowed_index_name_length` is no longer relied on by any adapter,
      so I'd like to remove the internal specific method which is no longer
      used.
      ab2d859e
  24. 27 4月, 2020 1 次提交
    • R
      Deprecate `in_clause_length` in `DatabaseLimits` · c0ca7625
      Ryuta Kamizono 提交于
      `in_clause_length` was added at c5a284f8 to address to Oracle IN clause
      length limitation.
      
      Now `in_clause_length` is entirely integrated in Arel visitor since
      #35838 and #36074.
      
      Since Oracle visitors are the only code that rely on `in_clause_length`.
      so I'd like to remove that from Rails code base, like has removed Oracle
      visitors (#38946).
      c0ca7625
  25. 25 4月, 2020 2 次提交
  26. 20 4月, 2020 1 次提交
  27. 16 4月, 2020 1 次提交
    • E
      Add `if_exists` option to `remove_index` · 36ea1084
      eileencodes 提交于
      This PR allows for passing `if_exists` options to the `remove_index`
      method so that we can ignore already removed indexes. This work follows
      column `if/if_not_exists` from #38352 and `:if_not_exists` on `add_index`
      from #38555.
      
      We've found this useful at GitHub, there are migrations where we don't
      want to raise if an index was already removed. This will allow us to
      remove a monkey patch on `remove_index`.
      
      I considered raising after the `index_name_for_remove` method is called
      but that method will raise if the index doesn't exist before we get to
      execute. I have a commit that refactors this but after much
      consideration this change is cleaner and more straightforward than other
      ways of implementing this.
      
      This change also adds a little extra validation to the `add_index` test.
      Fix `nodoc` on edited methods.
      36ea1084
  28. 15 4月, 2020 2 次提交
  29. 14 4月, 2020 1 次提交
    • J
      Prevent has_one from touching parent record unless persisted · ba3ef762
      Josh 提交于
      Previously, if `build_association` was called multiple times for a `has_one` association but never committed to the database, the first newly-associated record would trigger `touch` during the attempted removal of the record.
      
      For example:
      
          class Post < ActiveRecord::Base
            has_one :comment, inverse_of: :post, dependent: :destroy
          end
      
          class Comment < ActiveRecord::Base
            belongs_to :post, inverse_of: :comment, touch: true
          end
      
          post = Post.create!
          comment_1 = post.build_comment
          comment_2 = post.build_comment
      
      When `comment_2` is initialized, the `has_one` would attempt to destroy `comment_1`, triggering a `touch` on `post` from an association record that hasn't been committed to the database.
      
      This removes the attempt to delete an associated `has_one` unless it’s persisted.
      ba3ef762