circuithub / rel8

Hey! Hey! Can u rel8?
https://rel8.readthedocs.io
Other
149 stars 38 forks source link

Inserts segfault when run in cabal repl #325

Open tysonzero opened 1 month ago

tysonzero commented 1 month ago

They work fine when compiled, and select statements work correctly when compiled and also in the repl.

Environment information:

Setup:

stmt = insert $ Insert
    { into = testSchema
    , rows = values [Test ...]
    , onConflict = DoNothing
    , returning = NoReturning
    }

Hasql.Session.run (Hasql.Session.statement () $ Rel8.run_ stmt) conn 

Putting rows = values @_ @[] undefined still segfaults, but rows = undefined will run into an undefined error instead before hitting any segfault.

tysonzero commented 1 month ago

Sadly using hasql-pool and/or hasql-transaction does not circumvent this issue. However using the raw sql function to do the insert does.

tysonzero commented 1 month ago

Narrowed it down quite a bit further. A simple seq stmt (pure ()) segfaults also, but if you remove the insert and switch the type accordingly then seq stmt (pure ()) does not segfault, so insert is where the segfault lives.

tysonzero commented 1 month ago

Inserting with opaleye does not segfault, so it seems to be specific to something rel8 is doing.

ulidtko commented 3 weeks ago

Can also reproduce in stack repl, by simply pretty-printing putStrLn . showInsert — without ever trying to execute the query.

@tysonzero I don't think it's anything concerning in the rel8 library or dependencies. I think it's a bug in GHCi. As you noticed: when compiled, the code works fine; the segfaults only happens at the REPL.

As a workaround, you may have luck with loading your modules after :set -fobject-code. Per my testing, this stops the crashes. It switches GHCi from "interpreter mode" to loading compiled modules... the GHC User Guide has more to say.

Another way to work around is to :load the entire rel8 package source intepreted into ghci (e.g. git clone rel8 && cabal repl) — then crashing stops too.

The segfaults occur when you mix compiled + interpreted code:

and both workarounds avoid executing this mix. Either all compiled, or all interpreted.

(reproducer & debug info) ```haskell {-# LANGUAGE PackageImports #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DisambiguateRecordFields #-} module Main where import "base" Prelude import "rel8" Rel8 test :: Insert () test = Insert { into=schema, rows=mempty, onConflict=DoNothing, returning=NoReturning } where schema :: TableSchema (Name String, Name String) schema = TableSchema {name="test", columns=("colA","colB")} main :: IO () main = crash where crash = putStrLn . showInsert $ test -- runs normally when compiled -- runs normally in ghci with :set -fobject-code -- runs normally when the entire rel8 package is ghci-loaded as interpreted -- SIGSEGV in ghci (cabal repl/stack repl/runhaskell) if interpreted + rel8 compiled ``` gdb backtrace: ``` gef➤ bt #0 0x00007fe8bc9b6df0 in rel8zm1zi5zi0zi0zmEtDbQcfqXPcGsiIyfg6qgY_Rel8ziTable_zdp1Table_info () from /home/ulidtko/.stack/snapshots/x86_64-linux/fdaef2aa8bc81d41b10e82e9af70d91504e70836800b4357fa5e12ea48e4f220/9.6.4/lib/x86_64-linux-ghc-9.6.4/libHSrel8-1.5.0.0-EtDbQcfqXPcGsiIyfg6qgY-ghc9.6.4.so #1 0x0000000000000000 in ?? () ``` ... which isn't particularly enlightening (as I'm not yet using `-DDEBUG` GHCi); demangled: > **rel8-1.5.0.0-EtDbQcfqXPcGsiIyfg6qgY_Rel8.Table_$p1Table_info** refers to the `Table` class infotable...
TeofilC commented 3 weeks ago

@ulidtko thank you for investigating! Could you make an issue on the GHC bug tracker? Also is this reproducible with a newer GHC?

If you don't have the time right now, I can make an issue.

ulidtko commented 3 weeks ago

Hi @TeofilC!

Trying to retest on GHC HEAD today, but repro involves installing rel8 as a compiled package — so, far from minimal...

Please go ahead with filing an issue; I only found #22956 for 9.6 but it's unclear if underlying cause is the same.

TeofilC commented 3 weeks ago

GHC ticket: https://gitlab.haskell.org/ghc/ghc/-/issues/24943

TeofilC commented 1 week ago

To save folks reading the GHC issue. This is a known bug in GHC and there is a fix, which should be extensively backported. So hopefully this should be fixed in the next minor release of maintained major versions of GHC

ulidtko commented 2 days ago

Fix has landed to GHC master & I retested it, works indeed:

GHCi, version 9.11.20240628: https://www.haskell.org/ghc/  :? for help
[1 of 2] Compiling Main             ( crasher-rel8#325.hs, interpreted )
Ok, one module loaded.
ghci> :main
INSERT INTO "test" ("colA",
                    "colB")
SELECT
CAST(CAST(NULL AS "bpchar"(1)[]) AS "bpchar"(1)[]) as "_1",
CAST(CAST(NULL AS "bpchar"(1)[]) AS "bpchar"(1)[]) as "_2"
FROM (SELECT
      *
      FROM (SELECT
            0) AS "T1"
      WHERE (CAST(FALSE AS boolean))) AS "T1"
ON CONFLICT DO NOTHING

But if I understand correctly, backporting it to 9.6, 9.8, 9.10 remains to be done.