groue / GRDB.swift

A toolkit for SQLite databases, with a focus on application development

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Assertion failed: unexpected NULL value

ahartman opened this issue · comments

Dear Gwendal,

Please find attached my app and the entry in Library/Containers that holds the database.
I commented out everything not necessary for you in the app.
You will see a menu ‘Commands’, bringing you to a button ‘get timeline’; that button will trigger the error in DBModel().getPatientTimeline().

I hope this helps you, let me know if need something else.

Met vriendelijke groeten, Kind regards, Mes meilleures salutations,
André Hartman

Archief.zip

Dear Gwendal,
This morning my app crashes at startup, please find Crash Report attached.
If I take the code and run from Xcode, it will not crash.

This has to do with the same PatientTimeLine function that has the NULL value issues.
Hope this helps.
Regards, André Hartman
Crash Report.txt

🎁 Thank you very much @ahartman,

This sample project and data indeed reveals a bug in GRDB. This bug prevents a normal error to be reported to your app. Next version will fix this.

Until then, I commented out the assertion, and the error is now correctly reported to your app (caught in DBModel.getPatientTimeline()):

could not decode Date from database value NULL
column: "minVisitVisitCreated"
column index: 2
row: [id:2289 patientName:"Aaliyah" minVisitVisitCreated:NULL maxVisitVisitDate:NULL]
sql: SELECT
       "patient"."id", "patient"."patientName",
       MIN("visit"."visitCreated") AS "minVisitVisitCreated",
       MAX("visit"."visitDate") AS "maxVisitVisitDate"
     FROM "patient"
     LEFT JOIN "visit"
       ON ("visit"."patientId" = "patient"."id")
       AND ("visit"."visitCalendar" IN (?, ?)) AND ("visit"."visitCalendar" IN (?, ?))
     GROUP BY "patient"."id"
     ORDER BY "patient"."patientName"
arguments: ["Marieke", "Marieke nieuwe", "Marieke", "Marieke nieuwe"]

So the database contains NULL in the minVisitVisitCreated column. This can happen when MIN("visit"."visitCreated") is NULL (if there is no filtered visit, for example).

This NULL value is decoded into PatientTimelineInfo, which has non-null dates:

struct PatientTimelineInfo: Decodable, FetchableRecord {
    var patient: PatientInfo.Patient
    var minVisitVisitCreated: Date
    var maxVisitVisitDate: Date
}

The fix (in your app) is to modify PatientTimelineInfo so that its dates are optional:

struct PatientTimelineInfo: Decodable, FetchableRecord {
    var patient: PatientInfo.Patient
    var minVisitVisitCreated: Date? // nil when there is no visit
    var maxVisitVisitDate: Date?    // nil when there is no visit
}

I'll take care of the bug in GRDB that prevented the correct error to be reported in the first place. 👍

Oh, yes, thanks for reminding me about this.

So I restored the .having(filteredVisits.isEmpty == false) line.

It runs correctly.

The SQL looks valid, if not ideal (note the repetition of the "visit"."visitCalendar" IN (?, ?) condition, which is superfluous):

SELECT
  "patient"."id", "patient"."patientName",
  MIN("visit"."visitCreated") AS "minVisitVisitCreated",
  MAX("visit"."visitDate") AS "maxVisitVisitDate"
FROM "patient"
LEFT JOIN "visit"
  ON ("visit"."patientId" = "patient"."id")
  AND ("visit"."visitCalendar" IN (?, ?))
  AND ("visit"."visitCalendar" IN (?, ?))
  AND ("visit"."visitCalendar" IN (?, ?))
GROUP BY "patient"."id"
HAVING COUNT(DISTINCT "visit"."rowid") > 0
ORDER BY "patient"."patientName"

Would you please remind me what prevents you from restoring the having clause? Now that I have a running app, it's easier to reproduce!