fukamachi / mito

An ORM for Common Lisp with migrations, relationships and PostgreSQL support
292 stars 31 forks source link

Proposal to check early for null values (on `find-dao` and friends) #91

Open vindarel opened 3 years ago

vindarel commented 3 years ago

Hi,

When I call find-dao 'my-model :my-slot (get-data) but (get-data) is buggy and returns NIL instead of a value, I get a large stacktrace which goes down to SXQL:

There is no primary method for the generic function
  #<STANDARD-GENERIC-FUNCTION SXQL.SQL-TYPE:YIELD (58)>
when called with arguments
  (NIL).
   [Condition of type SB-PCL::NO-PRIMARY-METHOD-ERROR]
See also:
  Common Lisp Hyperspec, 7.6.6.2 [:section]

Restarts:
 0: [RETRY] Retry calling the generic function.
 1: [RETRY] Retry SLIME REPL evaluation request.
 2: [*ABORT] Return to SLIME's top level.
 3: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1005F41B63}>)

Backtrace:
  0: ((:METHOD SB-PCL::NO-PRIMARY-METHOD (T)) #<STANDARD-GENERIC-FUNCTION SXQL.SQL-TYPE:YIELD (58)> NIL) [fast-method]
  1: (SB-PCL::CALL-NO-PRIMARY-METHOD #<STANDARD-GENERIC-FUNCTION SXQL.SQL-TYPE:YIELD (58)> (NIL))
  2: ((:METHOD SXQL.SQL-TYPE:YIELD (SXQL.SQL-TYPE:INFIX-OP)) #<error printing object>) [fast-method]
  3: ((:METHOD SXQL.SQL-TYPE:YIELD :AROUND (T)) #<error printing object>) [fast-method]
  4: ((:METHOD SXQL.SQL-TYPE:YIELD (SXQL.SQL-TYPE:CONJUNCTIVE-OP)) #<error printing object>) [fast-method]
  5: ((:METHOD SXQL.SQL-TYPE:YIELD :AROUND (T)) #<error printing object>) [fast-method]
  6: ((:METHOD SXQL.SQL-TYPE:YIELD (SXQL.SQL-TYPE:CONJUNCTIVE-OP)) #<error printing object>) [fast-method]
  7: ((:METHOD SXQL.SQL-TYPE:YIELD :AROUND (T)) #<error printing object>) [fast-method]
  8: ((:METHOD SXQL.SQL-TYPE:YIELD (SXQL.SQL-TYPE:EXPRESSION-CLAUSE)) #<error printing object>) [fast-method]
  9: ((:METHOD SXQL.SQL-TYPE:YIELD :AROUND (T)) #<error printing object>) [fast-method]
 10: ((:METHOD SXQL.SQL-TYPE:YIELD (SXQL.SQL-TYPE:SQL-COMPOSED-STATEMENT)) #<error printing object>) [fast-method]
 11: ((:METHOD SXQL.SQL-TYPE:YIELD (SXQL.STATEMENT:SELECT-STATEMENT)) #<error printing object>) [fast-method]
 12: ((SB-PCL::EMF SXQL.SQL-TYPE:YIELD) #<error printing object>)
 13: ((:METHOD MITO.DB:RETRIEVE-BY-SQL (SXQL.SQL-TYPE:SQL-STATEMENT)) #<error printing object>) [fast-method]
 14: ((SB-PCL::EMF MITO.DB:RETRIEVE-BY-SQL) #<error printing object>)
 15: (MITO.DAO:SELECT-BY-SQL #<error printing object>)
 16: (MITO.DAO:FIND-DAO #<MITO.DAO.TABLE:DAO-TABLE-CLASS FEL/MODELS::AUTHOR> :ISNI NIL)
 17: (MAKE-CARD-FROM-DICT #<HASH-TABLE :TEST EQUAL :COUNT 31 {1002316D93}> :DB-SAVE NIL :FORCE T)
 18: (SB-INT:SIMPLE-EVAL-IN-LEXENV (MAKE-CARD-FROM-DICT *CARD* :FORCE T) #<NULL-LEXENV>)

I propose to check early for null values in find-dao (and friends) to get a smaller stacktrace like this one:

The value for the slot :ISNI should not be null (NIL)
   [Condition of type SIMPLE-ERROR]

Restarts:
 0: [RETRY] Retry SLIME REPL evaluation request.
 1: [*ABORT] Return to SLIME's top level.
 2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1005F41B63}>)

Backtrace:
  0: (MITO.DAO::WHERE-AND (:ISNI NIL) #<MITO.DAO.TABLE:DAO-TABLE-CLASS FEL/MODELS::AUTHOR>)
  1: (MITO.DAO:FIND-DAO #<MITO.DAO.TABLE:DAO-TABLE-CLASS FEL/MODELS::AUTHOR> :ISNI NIL)
  2: (MAKE-CARD-FROM-DICT #<HASH-TABLE :TEST EQUAL :COUNT 31 {1002316D93}> :DB-SAVE NIL :FORCE T)
  3: (SB-INT:SIMPLE-EVAL-IN-LEXENV (MAKE-CARD-FROM-DICT *CARD* :FORCE T) #<NULL-LEXENV>)

I got this by adding this check in the where-and function:

                    unless slot
                      do (error "Class ~S does not have a slot named ~S" class field)
                    unless value  ;; <-- added
                      do (error "The value for the slot ~S should not be null (~S)" field value)

What do you think and how to check this in select-dao (it doesn't use where-and)?

Best,