valderman / selda

A type-safe, high-level SQL library for Haskell
https://selda.link
MIT License
478 stars 58 forks source link

Cannot insert with an autoPrimaryKey without throwing DefaultValueException #193

Closed tankorsmash closed 7 months ago

tankorsmash commented 7 months ago

Thanks for the library! It's been nice to use and I was happy to follow along the official tutorial too!

I followed the insert docs as well as I could, but the following code throws a DefaultValueException instead of inserting the rows into the SQLite database.

The docs for def say:

Using def in any other context than insertion results in a runtime error.

which is what I believe I'm doing; simply using it in an insert call.

Interestingly, if I remove the someInt field on DataRow the now-single-field DataRow, it'll insert, but not if I give it a second or more fields. My actual project has a dozen fields, and it didn't seem to matter which fields were there with the id.

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-}

module Main (main) where

import Database.Selda as Selda
import Database.Selda.SQLite

import Prelude (IO, Int, print, putStrLn, ($))

data DataRow = DataRow
    { arbitraryId :: !(ID DataRow)
    , someInt :: !Prelude.Int
    }
    deriving (Generic)
instance SqlRow DataRow

dataRows = Selda.table "datarows" [#arbitraryId :- autoPrimary]

testDb = do
    -- comment this creation bit out the second time running
    print ("creating table" :: Text)
    _ <-
        withSQLite "./databaseFile.db" $
            createTable dataRows

    print ("inserting rows" :: Text)
    _ <-
        withSQLite "./databaseFile.db" $
            insert
                dataRows
                [ DataRow
                    { Main.arbitraryId = def :: ID DataRow
                    , Main.someInt = 123
                    }
                ]

    print ("done" :: Text)
main :: IO ()
main = testDb

I created a new project:

$ cabal init
$ cabal install selda selda-sqlite --lib # (docs don't include '--lib' but it wouldnt install otherwise)

$ nvim ../app/Main.hs
# paste the above code in to Main.hs

# add selda and selda-sqlite as dependencies:
$ nvim <PROJECT-NAME>.cabal

    build-depends:
        base ^>=4.14.3.0,
        selda,
        selda-sqlite

$ cabal run

I'm using cabal-install version 3.6.2.0, GHC 8.10.7, selda-0.5.2.0 and selda-sqlite-0.1.7.2. If there's more info I can attach, please let me know! I'm not familiar enough with Haskell yet to make a good issue

tankorsmash commented 7 months ago

Sorted this one out! It seems to be that because my arbitraryId field was marked as strict with ! it gets evaluated immediately which seems to be part of the issue.

For any other Haskell newbies, I cloned Selda locally, I appended

executable sqlite-bug-repro
    main-is:          Main.hs

    build-depends:
        base >=4.10,
        selda,
        selda-sqlite
    hs-source-dirs:   app
    default-language: Haskell2010

    ghc-options: -O0 -g

to selda/selda-sqlite/selda-sqlite.cabal and added the above file to selda/selda-sqlite/app/Main.hs. Then I added tons of Debug.Trace.trace "some string" calls everywhere to try and figure out why the GHCI debugger (via cabal v2-repl sqlite-bug-report) was showing <unknown> after I used :set -fbreak-on-exception to run :trace testDb.

I had also found a SO answer with a bunch of cool stuff I added to my ~/.ghci.

Anyway, it seems like the reason it was <unknown> was because the body of testDb wasnt unpacked enough. I moved the DataRow initting into a let binding and the traceback suddenly showed it as the culprit:


    let dataRow :: DataRow = DataRow { Main.arbitraryId = def :: ID DataRow
        , Main.someInt = 123
        }
     in
     withSQLite "./databaseFile.db" $
        insert
                dataRows
                [ dataRow
                ]

I also have the following appended to selda's cabal.project at the end of the file, I'm not sure if it helped:


optimization: 0
debug-info: True

package selda
    ghc-options: -O0 -g
package selda-sqlite
    ghc-options: -O0 -g

Thanks again for the library, hope this helps someone else too!