Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clear Compilation Error on Eager Loading #62

Closed
russ opened this issue Jan 9, 2019 · 4 comments
Closed

Clear Compilation Error on Eager Loading #62

russ opened this issue Jan 9, 2019 · 4 comments

Comments

@russ
Copy link
Contributor

russ commented Jan 9, 2019

I've recreated a small example here https://gist.github.com/russ/e6353ac39b0c7b0322f336a47c273b7e

The error started happening with the 0.5 release.

This is the output:

Error in test.cr:56: instantiating 'Video::Collection#with_actors()'

puts VideoRelease.query.with_video(&.with_actors).first.inspect
                                     ^~~~~~~~~~~

in macro '__generate_relations' /Users/russ/Desktop/associations/lib/clear/src/clear/model/modules/has_relations.cr:115, line 9:

   1.
   2.
   3.
   4.         Relations::HasManyMacro.generate(Video, performances, Performance, video_id,
   5.           nil)
   6.
   7.
   8.
>  9.         Relations::HasManyThroughMacro.generate(Video, actors, Actor, Performance,
  10.           video_id, actor_id)
  11.
  12.
  13.
  14.         Relations::HasManyMacro.generate(Video, video_releases, VideoRelease, releaseable_id,
  15.           nil)
  16.
  17.
  18.
  19.

expanding macro
in macro 'generate' /Users/russ/Desktop/associations/lib/clear/src/clear/model/modules/relations/has_many_through_macro.cr:4, line 78:

   1.     def actors : Actor::Collection
   2.       __temp_56 = Actor.table
   3.       __temp_57 = Actor.pkey
   4.
   5.       __temp_58 =
   6.         Performance.table
   7.
   8.
   9.       __temp_59 =  "actor_id"
  10.
  11.       __temp_60 =  "video_id"
  12.
  13.       cache = @cache
  14.
  15.       qry = Actor.query.select("#{Clear::SQL.escape(__temp_56)}.*")
  16.         .join(Clear::SQL.escape(__temp_58)){
  17.           var(__temp_58, __temp_59) == var(__temp_56, __temp_57)
  18.         }.where{
  19.           # FIXME: self.id or self.pkey ?
  20.           var(__temp_58, __temp_60) == self.id
  21.         }.distinct("#{Clear::SQL.escape(__temp_56)}.#{Clear::SQL.escape(__temp_57)}")
  22.
  23.
  24.       if cache && cache.active?("actors")
  25.         arr = cache.hit("actors", self.pkey, Actor)
  26.         qry.with_cached_result(arr)
  27.       end
  28.
  29.       qry
  30.     end
  31.
  32.     # Addition of the method for eager loading and N+1 avoidance.
  33.     class Collection
  34.       # Eager load the relation actors.
  35.       # Use it to avoid N+1 queries.
  36.       def with_actors(&block : Actor::Collection -> ) : self
  37.         before_query do
  38.           __temp_56 = Actor.table
  39.           __temp_57 = Actor.pkey
  40.           __temp_58 = Performance.table
  41.           __temp_59 = actor_id || Actor.table.to_s.singularize + "_id"
  42.           __temp_60 = video_id || Video.table.to_s.singularize + "_id"
  43.           self_type = Video
  44.
  45.           @cache.active "actors"
  46.
  47.           sub_query = self.dup.clear_select.select("#{Video.table}.#{self_type.pkey}")
  48.
  49.           qry = Actor.query.join(__temp_58){
  50.             var(__temp_58, __temp_59) == var(__temp_56, __temp_57)
  51.           }.where{
  52.             var(__temp_58, __temp_60).in?(sub_query)
  53.           }.distinct.select( "#{Clear::SQL.escape(__temp_56)}.*",
  54.             "#{Clear::SQL.escape(__temp_58)}.#{Clear::SQL.escape(__temp_60)} AS __own_id"
  55.           )
  56.
  57.           block.call(qry)
  58.
  59.           h = {} of Clear::SQL::Any => Array(Actor)
  60.
  61.           qry.each(fetch_columns: true) do |mdl|
  62.             unless h[mdl.attributes["__own_id"]]?
  63.               h[mdl.attributes["__own_id"]] = [] of Actor
  64.             end
  65.
  66.             h[mdl.attributes["__own_id"]] << mdl
  67.           end
  68.
  69.           h.each do |key, value|
  70.             @cache.set("actors", key, value)
  71.           end
  72.         end
  73.
  74.         self
  75.       end
  76.
  77.       def with_actors
> 78.         with_actors{}
  79.       end
  80.
  81.     end
  82.
  83.

instantiating 'with_actors()'
in macro '__generate_relations' /Users/russ/Desktop/associations/lib/clear/src/clear/model/modules/has_relations.cr:115, line 9:

   1.
   2.
   3.
   4.         Relations::HasManyMacro.generate(Video, performances, Performance, video_id,
   5.           nil)
   6.
   7.
   8.
>  9.         Relations::HasManyThroughMacro.generate(Video, actors, Actor, Performance,
  10.           video_id, actor_id)
  11.
  12.
  13.
  14.         Relations::HasManyMacro.generate(Video, video_releases, VideoRelease, releaseable_id,
  15.           nil)
  16.
  17.
  18.
  19.

expanding macro
in macro 'generate' /Users/russ/Desktop/associations/lib/clear/src/clear/model/modules/relations/has_many_through_macro.cr:4, line 41:

   1.     def actors : Actor::Collection
   2.       __temp_56 = Actor.table
   3.       __temp_57 = Actor.pkey
   4.
   5.       __temp_58 =
   6.         Performance.table
   7.
   8.
   9.       __temp_59 =  "actor_id"
  10.
  11.       __temp_60 =  "video_id"
  12.
  13.       cache = @cache
  14.
  15.       qry = Actor.query.select("#{Clear::SQL.escape(__temp_56)}.*")
  16.         .join(Clear::SQL.escape(__temp_58)){
  17.           var(__temp_58, __temp_59) == var(__temp_56, __temp_57)
  18.         }.where{
  19.           # FIXME: self.id or self.pkey ?
  20.           var(__temp_58, __temp_60) == self.id
  21.         }.distinct("#{Clear::SQL.escape(__temp_56)}.#{Clear::SQL.escape(__temp_57)}")
  22.
  23.
  24.       if cache && cache.active?("actors")
  25.         arr = cache.hit("actors", self.pkey, Actor)
  26.         qry.with_cached_result(arr)
  27.       end
  28.
  29.       qry
  30.     end
  31.
  32.     # Addition of the method for eager loading and N+1 avoidance.
  33.     class Collection
  34.       # Eager load the relation actors.
  35.       # Use it to avoid N+1 queries.
  36.       def with_actors(&block : Actor::Collection -> ) : self
  37.         before_query do
  38.           __temp_56 = Actor.table
  39.           __temp_57 = Actor.pkey
  40.           __temp_58 = Performance.table
> 41.           __temp_59 = actor_id || Actor.table.to_s.singularize + "_id"
  42.           __temp_60 = video_id || Video.table.to_s.singularize + "_id"
  43.           self_type = Video
  44.
  45.           @cache.active "actors"
  46.
  47.           sub_query = self.dup.clear_select.select("#{Video.table}.#{self_type.pkey}")
  48.
  49.           qry = Actor.query.join(__temp_58){
  50.             var(__temp_58, __temp_59) == var(__temp_56, __temp_57)
  51.           }.where{
  52.             var(__temp_58, __temp_60).in?(sub_query)
  53.           }.distinct.select( "#{Clear::SQL.escape(__temp_56)}.*",
  54.             "#{Clear::SQL.escape(__temp_58)}.#{Clear::SQL.escape(__temp_60)} AS __own_id"
  55.           )
  56.
  57.           block.call(qry)
  58.
  59.           h = {} of Clear::SQL::Any => Array(Actor)
  60.
  61.           qry.each(fetch_columns: true) do |mdl|
  62.             unless h[mdl.attributes["__own_id"]]?
  63.               h[mdl.attributes["__own_id"]] = [] of Actor
  64.             end
  65.
  66.             h[mdl.attributes["__own_id"]] << mdl
  67.           end
  68.
  69.           h.each do |key, value|
  70.             @cache.set("actors", key, value)
  71.           end
  72.         end
  73.
  74.         self
  75.       end
  76.
  77.       def with_actors
  78.         with_actors{}
  79.       end
  80.
  81.     end
  82.
  83.

undefined local variable or method 'actor_id'```
@anykeyh
Copy link
Owner

anykeyh commented Jan 9, 2019

I will review it this week-end when I have time. I'm wondering if you use the table name as string instead of realtions in has_many through:, wouldn't be working? (I think I have no spec with relations as model even if the code should make it works).

@russ
Copy link
Contributor Author

russ commented Jan 9, 2019

I tried both ways actually and ended up with the same error.

@anykeyh
Copy link
Owner

anykeyh commented Jan 10, 2019

I've just fixed, but without spec this time (due to overloaded planning, I had only 15mn to offer today 😄 ). Basically it compiles but I didn't check if the code is working (it should however).

Feel free to reopen if the fix is not working.

By the way, I'm in the process to use Clear in production-grade software for my own company in few weeks/months. So basically it will become production-ready ASAP 👍 .

Regards,

@russ
Copy link
Contributor Author

russ commented Jan 10, 2019

Works on my end. 👍

We've been using clear in production for almost 3 months now and it's been really nice. The code base is really stable and fast.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants