mysql-haskell中try函数Right分支未触发问题排查求助
mysql-haskell插入成功但请求挂起,分支未触发问题排查与解决
问题场景
使用servant和mysql-haskell编写服务器时,执行INSERT语句遇到异常行为:
- 新用户插入请求:数据成功写入数据库,但Right分支代码未执行(控制台无"No exception occurred"输出),客户端请求一直挂起
- 重复插入触发唯一键冲突:Left分支正常执行,控制台输出错误信息,客户端能收到响应
代码片段
... result <- try $ M.query conn ("insert into `user` (`email`,`name`,`date_joined`) values (?, ?, NOW());") [M.One $ M.MySQLText "1@mail.ru", M.One $ M.MySQLText "username"] case result of Right (defs,is) -> do putStrLn "No exception occurred" print =<< Streams.toList is Left e -> putStrLn ("Caught exception: " ++ show (e :: M.ERRException)) ...
环境信息
GHCi, version 9.4.8 mysql Ver 8.0.36 for Linux on x86_64
问题原因
mysql-haskell的query函数设计用于处理会返回结果集的SQL语句(如SELECT),它会等待读取完整的结果流。但不带RETURNING子句的INSERT语句不会返回任何结果集,此时Streams.toList is会一直阻塞,等待不存在的数据流,导致Right分支后续代码无法执行,线程挂起,客户端请求也因此一直处于等待状态。
而触发唯一键冲突时,数据库直接返回错误,try捕获异常后进入Left分支,不需要处理结果流,所以流程能正常完成,客户端收到响应。
解决方法
方案1:用execute替代query(推荐)
mysql-haskell提供了execute函数,专门用于执行无结果返回的SQL语句(INSERT/UPDATE/DELETE等),它执行完成后直接返回受影响的行数,不会阻塞等待结果流:
... result <- try $ M.execute conn "insert into `user` (`email`,`name`,`date_joined`) values (?, ?, NOW());" [M.One $ M.MySQLText "1@mail.ru", M.One $ M.MySQLText "username"] case result of Right affectedRows -> do putStrLn $ "插入成功,影响行数:" ++ show affectedRows Left e -> putStrLn $ "Caught exception: " ++ show (e :: M.ERRException) ...
方案2:添加RETURNING子句获取结果(如需返回插入数据)
如果需要获取插入后的自增主键或其他字段,可以在INSERT语句后添加RETURNING子句,让数据库返回结果集,此时query和Streams.toList is能正常读取数据,不会阻塞:
... -- 假设主键列名为id,根据实际表结构调整 result <- try $ M.query conn "insert into `user` (`email`,`name`,`date_joined`) values (?, ?, NOW()) RETURNING id;" [M.One $ M.MySQLText "1@mail.ru", M.One $ M.MySQLText "username"] case result of Right (defs,is) -> do putStrLn "No exception occurred" rows <- Streams.toList is print rows Left e -> putStrLn $ "Caught exception: " ++ show (e :: M.ERRException) ...
内容的提问来源于stack exchange,提问作者Dmitry M
相关产品推荐
相关产品推荐

