aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
Commit message (Collapse)AuthorAgeFilesLines
...
* | | | | | Merge pull request #30809 from meganemura/use-calleeRafael França2017-10-051-1/+1
|\ \ \ \ \ \ | | | | | | | | | | | | | | Use __callee__ to pass alias instead of original method name
| * | | | | | Use __callee__ to pass alias instead of original method namemeganemura2017-10-051-1/+1
| |/ / / / / | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Before ``` > Article.left_joins ArgumentError: The method .left_outer_joins() must contain arguments. ``` After ``` > Article.left_joins ArgumentError: The method .left_joins() must contain arguments. ```
* | | | | | Move duplicated code to `delete_or_destroy` in `CollectionAssociation`Ryuta Kamizono2017-10-061-4/+2
| | | | | |
* | | | | | Add JSON attribute test cases for SQLite3 adapterRyuta Kamizono2017-10-052-1/+2
| | | | | |
* | | | | | Extract repeatedly appeared prepending compatible table definitionRyuta Kamizono2017-10-051-25/+24
|/ / / / /
* / / / / Prefer official name PostgreSQL over PostgresRyuta Kamizono2017-10-041-2/+2
|/ / / /
* | | | Ensure `AliasTracker` respects a custom table nameRyuta Kamizono2017-09-302-2/+2
| | | |
* | | | Merge pull request #30471 from dylanahsmith/remove-redundant-trackerAaron Patterson2017-09-272-24/+10
|\ \ \ \ | | | | | | | | | | activerecord: Remove a redundant mutation tracker
| * | | | activerecord: Remove a redundant mutation trackerDylan Thacker-Smith2017-08-302-24/+10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The extra mutation tracker was needed in Rails 5.1 to preserve the old behaviour of `changes`, but now there is no difference between `changes` and `changes_to_save`, so `@mutation_tracker` can be removed.
* | | | | `Postgres::OID::Range` serializes to a `Range`, quote in `Quoting`Thomas Cannon2017-09-262-1/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | PostgreSQL 9.1+ introduced range types, and Rails added support for using this datatype in ActiveRecord. However, the serialization of `PostgreSQL::OID::Range` was incomplete, because it did not properly quote the bounds that make up the range. A clear example of this is a `tsrange`. Normally, ActiveRecord quotes Date/Time objects to include the milliseconds. However, the way `PostgreSQL::OID::Range` serialized its bounds, the milliseconds were dropped. This meant that the value was incomplete and not equal to the submitted value. An example of normal timestamps vs. a `tsrange`. Note how the bounds for the range do not include their milliseconds (they were present in the ruby Range): UPDATE "iterations" SET "updated_at" = $1, "range" = $2 WHERE "iterations"."id" = $3 [["updated_at", "2017-09-23 17:07:01.304864"], ["range", "[2017-09-23 00:00:00 UTC,2017-09-23 23:59:59 UTC]"], ["id", 1234]] `PostgreSQL::OID::Range` serialized the range by interpolating a string for the range, which works for most cases, but does not work for timestamps: def serialize(value) if value.is_a?(::Range) from = type_cast_single_for_database(value.begin) to = type_cast_single_for_database(value.end) "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}" else super end end (byebug) from = type_cast_single_for_database(value.begin) 2010-01-01 13:30:00 UTC (byebug) to = type_cast_single_for_database(value.end) 2011-02-02 19:30:00 UTC (byebug) "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}" "[2010-01-01 13:30:00 UTC,2011-02-02 19:30:00 UTC)" @sgrif (the original implementer for Postgres Range support) provided some feedback about where the quoting should occur: Yeah, quoting at all is definitely wrong here. I'm not sure what I was thinking in 02579b5, but what this is doing is definitely in the wrong place. It should probably just be returning a range of subtype.serialize(value.begin) and subtype.serialize(value.end), and letting the adapter handle the rest. `Postgres::OID::Range` now returns a `Range` object, and `ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting` can now encode and quote a `Range`: def encode_range(range) "[#{type_cast(range.first)},#{type_cast(range.last)}#{range.exclude_end? ? ')' : ']'}" end ... encode_range(range) #=> "['2010-01-01 13:30:00.670277','2011-02-02 19:30:00.745125')" This commit includes tests to make sure the milliseconds are preserved in `tsrange` and `tstzrange` columns
* | | | | Don't generate `foreign_type` if `options[:polymorphic]` is not givenRyuta Kamizono2017-09-271-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | Because the reflection doesn't have `foreign_type` unless the association is a polymorphic association.
* | | | | Treat `Set` as an `Array` in `Relation#where`Sean Griffin2017-09-261-0/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | I do not want to set the expectation that any enumerable object should behave this way, but this case in particular comes up frequently enough that I'm caving on this one. Fixes #30684.
* | | | | PERF: Partially recover some performance when preloading.Guo Xiang Tan2017-09-264-15/+13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Benchmark Script: ``` require 'active_record' require 'benchmark/ips' ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL')) ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :users, force: true do |t| t.string :name, :email t.integer :topic_id t.timestamps null: false end create_table :topics, force: true do |t| t.string :title t.timestamps null: false end end attributes = { name: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', email: 'foobar@email.com' } class Topic < ActiveRecord::Base has_many :users end class User < ActiveRecord::Base belongs_to :topic end 100.times do User.create!(attributes) end users = User.first(50) Topic.create!(title: 'This is a topic', users: users) Benchmark.ips do |x| x.config(time: 10, warmup: 5) x.report("preload") do User.includes(:topic).all.to_a end end ``` Before: ``` Calculating ------------------------------------- preload 40.000 i/100ms ------------------------------------------------- preload 407.962 (± 1.5%) i/s - 4.080k ``` After: ``` alculating ------------------------------------- preload 43.000 i/100ms ------------------------------------------------- preload 427.567 (± 1.6%) i/s - 4.300k ```
* | | | | Merge pull request #30705 from frodsan/use-digest-thread-safeMatthew Draper2017-09-261-1/+1
|\ \ \ \ \ | | | | | | | | | | | | Preload digest/sha2 to avoid thread safe error.
| * | | | | Preload digest/sha2 to avoid thread safe error.Francesco Rodriguez2017-09-251-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | I got this error in production using Puma in multi-threaded mode: ``` RuntimeError: Digest::Base cannot be directly inherited in Ruby from active_support/security_utils.rb:23:in `variable_size_secure_compare' from active_support/security_utils.rb:23:in `hexdigest' from active_support/security_utils.rb:23:in `digest' ``` Looks like Digest uses const_missing to load Digest::SHA256 (https://github.com/ruby/ruby/blob/trunk/ext/digest/lib/digest.rb#L8) - https://bugs.ruby-lang.org/issues/9494 - https://github.com/ruby/ruby/commit/c02fa39463a0c6bf698b01bc610135604aca2ff4
* | | | | | PERF: Restore memoization when preloading associations.Guo Xiang Tan2017-09-251-6/+5
|/ / / / / | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Benchmark Script ``` require 'active_record' require 'benchmark/ips' require 'ruby-prof' require 'memory_profiler' require 'byebug' ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL')) ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :users, force: true do |t| t.string :name, :email t.integer :topic_id t.timestamps null: false end create_table :topics, force: true do |t| t.string :title t.timestamps null: false end end attributes = { name: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', email: 'foobar@email.com' } class Topic < ActiveRecord::Base has_many :users end class User < ActiveRecord::Base belongs_to :topic end 100.times do User.create!(attributes) end users = User.first(50) Topic.create!(title: 'This is a topic', users: users) Benchmark.ips do |x| x.config(time: 10, warmup: 5) x.report("preload") do User.includes(:topic).all.to_a end end ``` Before ``` Calculating ------------------------------------- preload 26.000 i/100ms ------------------------------------------------- preload 265.347 (± 3.0%) i/s - 2.652k ``` After ``` Calculating ------------------------------------- preload 39.000 i/100ms ------------------------------------------------- preload 406.053 (± 1.7%) i/s - 4.095k ```
* | | | | Extract `integer_like_primary_key_type` to ease to handle it for adaptersRyuta Kamizono2017-09-254-17/+18
| | | | |
* | | | | Move integer-like primary key normalization to `new_column_definition`Ryuta Kamizono2017-09-236-38/+28
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Currently the normalization only exists in `primary_key` shorthand. It should be moved to `new_column_definition` to also affect to `add_column` with primary key.
* | | | | Adding legacy primary key should be compatibleRyuta Kamizono2017-09-231-0/+19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Currently implicit legacy primary key is compatible, but adding explicit legacy primary key is not compatible. It should also be fixed. Fixes #30664.
* | | | | Ensure `1 AS one` for SQL Server with calculations.Ken Collins2017-09-221-1/+1
| | | | |
* | | | | Prevent extra `column_for` for `change_column_{default,null,comment}`Ryuta Kamizono2017-09-231-7/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `change_column_{default,null,comment}` in mysql2 adapter are passing `column.sql_type` as `type` to `change_column` to intend keeping previous type. But `column_for` requires extra query, so use passing `nil` to `type` explicitly in the internal for the purpose.
* | | | | `index_name` should be quotedRyuta Kamizono2017-09-221-1/+1
| | | | |
* | | | | Return nil if table comment is blankRyuta Kamizono2017-09-221-1/+1
| | | | |
* | | | | Implement change_table_comment and change_column_comment for MySql AdapterAlecs Popa2017-09-221-0/+10
| | | | |
* | | | | Use algorithm while removing index with db:rollbackMehmet Emin İNAÇ2017-09-211-2/+2
| | | | | | | | | | | | | | | | | | | | Closes #24190
* | | | | Merge pull request #30619 from ↵Eileen M. Uchitelle2017-09-201-4/+4
|\ \ \ \ \ | | | | | | | | | | | | | | | | | | | | | | | | jagthedrummer/jeremy/instrumentation-payload-names Update payload names for `sql.active_record` instrumentation to be more descriptive.
| * | | | | Update payload names for `sql.active_record` to be more descriptive.Jeremy Green2017-09-201-4/+4
| | | | | | | | | | | | | | | | | | | | | | | | Fixes #30586.
* | | | | | make create_join_table compatible.Yuki Masutomi2017-09-201-0/+15
| | | | | |
* | | | | | Merge pull request #30656 from yskkin/add_column_docRyuta Kamizono2017-09-201-0/+2
|\ \ \ \ \ \ | | | | | | | | | | | | | | Add :comment option for add_column [ci skip]
| * | | | | | Add :comment option for add_column [ci skip]Yoshiyuki Kinjo2017-09-201-0/+2
| |/ / / / /
* | | | | | Remove unused delegation to `reflection.options` in `Preloader::Association`Ryuta Kamizono2017-09-182-4/+1
| | | | | |
* | | | | | The name of the key on the associated record is abstracted as ↵Ryuta Kamizono2017-09-185-23/+14
| | | | | | | | | | | | | | | | | | | | | | | | `reflection.join_primary_key`
* | | | | | The name of the key on the owner is abstracted as `reflection.join_foreign_key`Ryuta Kamizono2017-09-184-17/+5
| | | | | |
* | | | | | Extract `associate_records_to_owner` to refactor `Preloader::Association`Ryuta Kamizono2017-09-183-20/+14
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Since we have `Preloader#preload`, `Preloader::Association#preload` is a little confusing. And also, since the `preload` method is an abstract method, it is hard to read where `associated_records_by_owner` is called. This refactors `Preloader::Association` to ease to read where `associated_records_by_owner` is called.
* | | | | | Fix collided sequence name detectionRyuta Kamizono2017-09-181-1/+8
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | If collided named sequence already exists, newly created serial column will generate alternative sequence name. Fix sequence name detection to allow the alternative names.
* | | | | | Remove the code that swapping `scope` and `options`Ryuta Kamizono2017-09-182-8/+3
| | | | | | | | | | | | | | | | | | | | | | | | `options` is never assigned to `scope` as long as using splat hash.
* | | | | | `id` (primary key) is not only an integer [ci skip]Ryuta Kamizono2017-09-181-1/+1
| | | | | |
* | | | | | Ensure returning affected objects for class level `update` and `destroy`Ryuta Kamizono2017-09-181-2/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Class level `update` and `destroy` are using `find` in the internal, so it will raise `RecordNotFound` if given ids cannot find an object even though the method already affect (update or destroy) to any objects. These methods should return affected objects even in that case.
* | | | | | Place class level `update`, `destroy`, and `delete` in ↵Ryuta Kamizono2017-09-183-95/+93
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `Persistence::ClassMethods` The docs are obviously for class level `update`, `destroy`, and `delete`. It should be placed in `Persistence::ClassMethods` rather than `Relation`. And also, these methods are not dependent on relation. So it is not needed to delegate to `all` (plus, `klass.find` is faster than `relation.find`).
* | | | | | Early return if `records.empty?` in `Preloader#preload`Ryuta Kamizono2017-09-181-3/+3
| | | | | |
* | | | | | Don't pass `reflection_scope` to `preload_scope` if `reflection.scope` isn't ↵Ryuta Kamizono2017-09-181-1/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | given Related 2b5f5cdd7c1d95716de6a206b6d09ccbb006dc17. If `reflection.scope` isn't given, `reflection_scope` is always empty scope. It is unnecessary to merge it.
* | | | | | Return `through_scope` only if the scope is not empty scopeRyuta Kamizono2017-09-181-4/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Related 2b5f5cdd7c1d95716de6a206b6d09ccbb006dc17. If `through_scope` is empty scope, it is unnecessary to merge it. And also, comparing relations is a little expensive (will cause `build_arel`). It is enough to use `empty_scope?` to determine whether empty scope.
* | | | | | Remove useless condition in `reset_association`Ryuta Kamizono2017-09-181-2/+1
| | | | | | | | | | | | | | | | | | | | | | | | `through_scope` is not empty scope if `options[:source_type]` is given.
* | | | | | Fix docs describing rollback [ci skip]dixpac2017-09-171-2/+2
|/ / / / / | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * `rails db:migrate STEP = 2` will not rollback the migrations, instead `rails db:rollback STEP = 2` will do the rollback. * Also, rewritten `rails db:migrate VERSION` => `rails db:rollback VERSION` for consistency.
* | | | | Remove unused explicit delegation to `klass` in `relation`Ryuta Kamizono2017-09-142-3/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | It is only used `primary_key` and `connection` in the internal, so it is not needed to delegate others to `klass` explicitly. This doesn't change public behavior because `relation` will delegate missing method to `klass`.
* | | | | Don't use `collection.table_name` in `collection_cache_key`Ryuta Kamizono2017-09-141-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | Because `collection.table_name` doesn't respect table alias. Use `collection.arel_attribute` instead.
* | | | | Don't use `quoted_table_name` in `limited_ids_for`Ryuta Kamizono2017-09-142-2/+8
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Because `quoted_table_name` doesn't respect table alias. We should use `arel_attribute` for that, so I added `column_name_from_arel_node` to generate column name from an arel node.
* | | | | `quoted_table_name` doesn't respect table aliasRyuta Kamizono2017-09-141-1/+1
| | | | | | | | | | | | | | | | | | | | So using `arel_attribute(primary_key).asc` in `batch_order` instead.
* | | | | Make `in_batches` queries to preparableRyuta Kamizono2017-09-141-3/+10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Before: ```sql SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > 2 ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > 4 ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > 6 ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > 8 ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > 10 ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] ``` After: ```sql SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > ? ORDER BY "posts"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > ? ORDER BY "posts"."id" ASC LIMIT ? [["id", 4], ["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > ? ORDER BY "posts"."id" ASC LIMIT ? [["id", 6], ["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > ? ORDER BY "posts"."id" ASC LIMIT ? [["id", 8], ["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > ? ORDER BY "posts"."id" ASC LIMIT ? [["id", 10], ["LIMIT", 2]] ```
* | | | | PERF: Incorrect memoization in ↵Guo Xiang Tan2017-09-111-1/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `ActiveRecord::Associations::Preloader::Association`. ``` require 'active_record' require 'benchmark/ips' ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL')) ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :users, force: true do |t| t.string :name, :email t.integer :topic_id t.timestamps null: false end create_table :topics, force: true do |t| t.string :title t.timestamps null: false end end attributes = { name: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', email: 'foobar@email.com' } class Topic < ActiveRecord::Base has_many :users end class User < ActiveRecord::Base belongs_to :topic end 100.times do User.create!(attributes) end users = User.first(50) 100.times do Topic.create!(title: 'This is a topic', users: users) end Benchmark.ips do |x| x.config(time: 10, warmup: 5) x.report("preload") do User.includes(:topic).all.to_a end end ``` ``` Calculating ------------------------------------- preload 25.000 i/100ms ------------------------------------------------- preload 251.772 (± 1.2%) i/s - 2.525k ``` ``` Calculating ------------------------------------- preload 26.000 i/100ms ------------------------------------------------- preload 270.392 (± 1.1%) i/s - 2.704k ```