Skip to content

Instantly share code, notes, and snippets.

@astery
Last active April 12, 2023 15:20
Show Gist options
  • Save astery/6137727 to your computer and use it in GitHub Desktop.
Save astery/6137727 to your computer and use it in GitHub Desktop.
Eager loading and custom select.
# Activate the gem you are reporting the issue against.
gem 'activerecord', '4.2.6'
require 'active_record'
require 'minitest/autorun'
require 'logger'
$logger = Logger.new(STDOUT)
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = $logger
ActiveRecord::Schema.define do
create_table :posts do |t|
t.string :name
t.string :teaser
end
create_table :comments do |t|
t.integer :post_id
t.string :content
t.string :user_name
end
end
class Post < ActiveRecord::Base
has_many :comments
has_many :comments_w_user_name, -> { select(:id, :user_name, :post_id) }, class_name: "Comment"
end
class Comment < ActiveRecord::Base
belongs_to :post
end
class BugTest < MiniTest::Unit::TestCase
def test_association_stuff
comment = Comment.create!
post = Post.create!(comments: [comment])
# Deprecated since 4.0.0 and will not work >4.2
# $logger.info "Post.includes(:comments).select(user_name)"
# Post.includes(:comments).select('posts.id', 'comments.id', 'comments.user_name').inspect
$logger.info "Post.includes(:comments_w_user_name)"
Post.includes(:comments_w_user_name).inspect
$logger.info "Post.includes(:comments).select(:user_name).references(:comments)"
Post.includes(:comments).select(:user_name).references(:comments).inspect
end
end
@astery
Copy link
Author

astery commented Oct 15, 2016

You can try it by your self just run with ruby -Ilib:test gistfile1.rb

For whose who lazy (like me):

  • activerecord 4.0.0
-- create_table(:posts)
D, [2016-10-15T19:14:34.212406 #7048] DEBUG -- :    (0.5ms)  CREATE TABLE "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "teaser" varchar(255)) 
   -> 0.0035s
-- create_table(:comments)
D, [2016-10-15T19:14:34.212887 #7048] DEBUG -- :    (0.1ms)  CREATE TABLE "comments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "post_id" integer, "content" varchar(255), "user_name" varchar(255)) 
   -> 0.0004s
Run options: --seed 1324

# Running tests:

D, [2016-10-15T19:14:34.217083 #7048] DEBUG -- :    (0.0ms)  begin transaction
D, [2016-10-15T19:14:34.224170 #7048] DEBUG -- :   SQL (0.1ms)  INSERT INTO "comments" DEFAULT VALUES
D, [2016-10-15T19:14:34.224422 #7048] DEBUG -- :    (0.1ms)  commit transaction
/home/akulov/.rvm/gems/ruby-2.3.0/gems/activerecord-4.0.0/lib/active_record/associations/has_many_association.rb:75: warning: circular argument reference - reflection
/home/akulov/.rvm/gems/ruby-2.3.0/gems/activerecord-4.0.0/lib/active_record/associations/has_many_association.rb:79: warning: circular argument reference - reflection
/home/akulov/.rvm/gems/ruby-2.3.0/gems/activerecord-4.0.0/lib/active_record/associations/has_many_association.rb:83: warning: circular argument reference - reflection
/home/akulov/.rvm/gems/ruby-2.3.0/gems/activerecord-4.0.0/lib/active_record/associations/has_many_association.rb:102: warning: circular argument reference - reflection
D, [2016-10-15T19:14:34.232318 #7048] DEBUG -- :    (0.1ms)  begin transaction
D, [2016-10-15T19:14:34.237370 #7048] DEBUG -- :   SQL (0.1ms)  INSERT INTO "posts" DEFAULT VALUES
D, [2016-10-15T19:14:34.239685 #7048] DEBUG -- :   SQL (1.4ms)  UPDATE "comments" SET "post_id" = ? WHERE "comments"."id" = 1  [["post_id", 1]]
D, [2016-10-15T19:14:34.240028 #7048] DEBUG -- :    (0.1ms)  commit transaction
I, [2016-10-15T19:14:34.240284 #7048]  INFO -- : Post.includes(:comments).select(user_name)
DEPRECATION WARNING: It looks like you are eager loading table(s) (one of: posts, comments) that are referenced in a string SQL snippet. For example: 

    Post.includes(:comments).where("comments.title = 'foo'")

Currently, Active Record recognizes the table in the string, and knows to JOIN the comments table to the query, rather than loading comments in a separate query. However, doing this without writing a full-blown SQL parser is inherently flawed. Since we don't want to write an SQL parser, we are removing this functionality. From now on, you must explicitly tell Active Record when you are referencing a table from a string:

    Post.includes(:comments).where("comments.title = 'foo'").references(:comments)

If you don't rely on implicit join references you can disable the feature entirely by setting `config.active_record.disable_implicit_join_references = true`. (called from test_association_stuff at test.rb:41)
D, [2016-10-15T19:14:34.244680 #7048] DEBUG -- :   SQL (0.2ms)  SELECT "posts"."id" AS t0_r0, "posts"."name" AS t0_r1, "posts"."teaser" AS t0_r2, "comments"."id" AS t1_r0, "comments"."post_id" AS t1_r1, "comments"."content" AS t1_r2, "comments"."user_name" AS t1_r3 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
I, [2016-10-15T19:14:34.245452 #7048]  INFO -- : Post.includes(:comments_w_user_name)
D, [2016-10-15T19:14:34.245759 #7048] DEBUG -- :   Post Load (0.1ms)  SELECT "posts".* FROM "posts"
D, [2016-10-15T19:14:34.248719 #7048] DEBUG -- :   Comment Load (0.1ms)  SELECT id, user_name, post_id FROM "comments" WHERE "comments"."post_id" IN (1)
I, [2016-10-15T19:14:34.248874 #7048]  INFO -- : Post.includes(:comments).select(:user_name).references(:comments)
D, [2016-10-15T19:14:34.249530 #7048] DEBUG -- :   SQL (0.1ms)  SELECT "posts"."id" AS t0_r0, "posts"."name" AS t0_r1, "posts"."teaser" AS t0_r2, "comments"."id" AS t1_r0, "comments"."post_id" AS t1_r1, "comments"."content" AS t1_r2, "comments"."user_name" AS t1_r3 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
.

Finished tests in 0.034347s, 29.1148 tests/s, 0.0000 assertions/s.

1 tests, 0 assertions, 0 failures, 0 errors, 0 skips
  • activerecord 4.2.6
-- create_table(:posts)
D, [2016-10-15T19:13:08.092981 #6903] DEBUG -- :    (0.3ms)  CREATE TABLE "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "teaser" varchar) 
   -> 0.0022s
-- create_table(:comments)
D, [2016-10-15T19:13:08.093558 #6903] DEBUG -- :    (0.1ms)  CREATE TABLE "comments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "post_id" integer, "content" varchar, "user_name" varchar) 
   -> 0.0003s
MiniTest::Unit::TestCase is now Minitest::Test. From test.rb:35:in `<main>'
Run options: --seed 808

# Running:

D, [2016-10-15T19:13:08.119210 #6903] DEBUG -- :    (0.1ms)  begin transaction
D, [2016-10-15T19:13:08.121013 #6903] DEBUG -- :   SQL (0.1ms)  INSERT INTO "comments" DEFAULT VALUES
D, [2016-10-15T19:13:08.121325 #6903] DEBUG -- :    (0.0ms)  commit transaction
D, [2016-10-15T19:13:08.132864 #6903] DEBUG -- :    (0.1ms)  begin transaction
D, [2016-10-15T19:13:08.133442 #6903] DEBUG -- :   SQL (0.1ms)  INSERT INTO "posts" DEFAULT VALUES
D, [2016-10-15T19:13:08.136725 #6903] DEBUG -- :   SQL (0.1ms)  UPDATE "comments" SET "post_id" = ? WHERE "comments"."id" = ?  [["post_id", 1], ["id", 1]]
D, [2016-10-15T19:13:08.137028 #6903] DEBUG -- :    (0.1ms)  commit transaction
I, [2016-10-15T19:13:08.137060 #6903]  INFO -- : Post.includes(:comments_w_user_name)
D, [2016-10-15T19:13:08.137443 #6903] DEBUG -- :   Post Load (0.1ms)  SELECT "posts".* FROM "posts"
D, [2016-10-15T19:13:08.141616 #6903] DEBUG -- :   Comment Load (0.1ms)  SELECT "comments"."id", "comments"."user_name", "comments"."post_id" FROM "comments" WHERE "comments"."post_id" IN (1)
I, [2016-10-15T19:13:08.141807 #6903]  INFO -- : Post.includes(:comments).select(:user_name).references(:comments)
D, [2016-10-15T19:13:08.148099 #6903] DEBUG -- :   SQL (0.2ms)  SELECT "user_name", "posts"."id" AS t0_r0, "posts"."name" AS t0_r1, "posts"."teaser" AS t0_r2, "comments"."id" AS t1_r0, "comments"."post_id" AS t1_r1, "comments"."content" AS t1_r2, "comments"."user_name" AS t1_r3 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
.

Finished in 0.035095s, 28.4938 runs/s, 0.0000 assertions/s.

1 runs, 0 assertions, 0 failures, 0 errors, 0 skips

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment