imdrasil/jennifer.cr

Eager loading causes DataTypeCasting exception on sqlite

wonderix opened this issue · 10 comments

Calling eager_load with sqlite always produces a DataTypeCasting Column <Model>.id can't be casted from Int64 to it's type - (Int32 | Nil).

I'm using a simple model with User, Team and Member and the following code is causing the exception

Team.all.eager_load(members: [:user])

This can be fixed by adding the following code in mapping.cr at line 438

--- a/src/jennifer/model/mapping.cr
+++ b/src/jennifer/model/mapping.cr
@@ -438,6 +438,8 @@ module Jennifer
               %casted_var{key.id} =
                 {% if value[:parsed_type] =~ /String/ %}
                   %var{key.id}
+                {% elsif value[:parsed_type] =~ /Int32/ %}
+                  (%var{key.id}.is_a?(Int64) ? %var{key.id}.to_i32 : %var{key.id})
                 {% else %}
                   (%var{key.id}.is_a?(String) ? self.class.coerce_{{key.id}}(%var{key.id}) : %var{key.id})
                 {% end %}

I'm not sure if this would be the right place for this kind of fix.

I'm using

  • crystal: 1.3.2
  • jennifer.cr: master
  • jennifer_sqlite3_adapter: 0.4.0

Hi, sorry for a delay. Does it mean that in all other cases everything is fine when you load your models separately? sqlite adapter master branch is behind current jennifer master branch. Recently we introduced a breaking change - automatic primary keys are of Int64 type.

Loading the models separately works fine. Only eager loading causes problems. I would say that my problem is related to this breaking change.

Will try to reproduce and fix on this weekend

Also could you please share minified mapping for your User, Team and Member models

class User < Jennifer::Model::Base
  mapping(
    id: Primary32,
    email: String?,
  )
  has_many :memberships, TeamMember
end

class TeamMember < Jennifer::Model::Base
  mapping(
    id: Primary32,
    user_id: Int32,
    team_id: Int32,
    role: Int32,
  )
  belongs_to :team, Team
  belongs_to :user, User
end

class Team < Jennifer::Model::Base
  mapping(
    id: Primary32,
    name: String,
  )
  has_many :members, TeamMember
end

Any updates on this issue?

I couldn't reproduce it with postgres. Maybe it's an issue with sqlite adapter.
Could you share your migration files?

Here's my test with postgres: cyangle@566fec0

@wonderix

Was on a vacation so it look longer to respond 🌴 Hm, I though I had done something with this but don't remember what and why I left this without a response. Will take a look in the evening today

I was able to reproduce it. Sorry, I don't remember why I skipped this 😞 . Will find a way to fix this behavior

The error still persists on latest releases
shards.yml

dependencies:
  kemal:
    github: kemalcr/kemal
  jennifer:
    github: imdrasil/jennifer.cr
    version: "~> 0.13.0"
  sqlite3:
    github: crystal-lang/crystal-sqlite3
    version: "0.18.0"
  jennifer_sqlite3_adapter:
    github: imdrasil/jennifer_sqlite3_adapter
    version: "~> 0.4.0"
  sam:
    github: imdrasil/sam.cr
    version: 0.4.2

Models:

class TravelPlan < Jennifer::Model::Base
  mapping(
    id: Primary32,
  )
  has_many :travel_stops, TravelStop, dependent: :destroy
end

class TravelStop < Jennifer::Model::Base
  mapping(
    id: Primary32,
    api_id: Int32,
    travel_plan_id: Int32?,
  )
  
  belongs_to :travel_plan, TravelPlan
end

Error: Exception: Column TravelPlan.id can't be casted from Int64 to it's type - (Int32 | Nil)
Code: travel_plan = TravelPlan.where { _id == id }.eager_load(:travel_stops).first!
Am I using the correct dependecy versions?