postgres-simple - 没有因使用“查询”而产生的(ToRow Int)实例

postgres-simple - No instance for (ToRow Int) arising from a use of ‘query’

我是 haskell 的新手,老实说我遇到了很多困难。但它扩展了我的思维,所以我们开始吧。 我正在尝试 运行 一个非常简单的 Web 服务器,它查询 Postgres 数据库并且应该 return 结果为 JSON。

查询非常简单: "Select id,data from MYTABLE where id = 1"

但是 haskell 的类型系统现在正在折磨我,我的行为的最终类型不匹配。 我正在使用 Spock and PostgreSQL-Simple 作为组合。

大多数教程要么对我想做的事情来说太简单,要么太难。我介于两者之间,错过了很多 Haskell 理解,很多我以前的问题我已经通过简单的复制和粘贴解决了,并且得到了一个简单的版本。

但是,一旦我尝试传递路由变量,我就失败了。 这是我的工作版本。我的数据库 Table 在这里称为 "Envelope",重要的调用是 get "json" :

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-}

module Main where

import Web.Spock
import Web.Spock.Config
import Database.PostgreSQL.Simple
import Data.Pool
import Data.Aeson (ToJSON(toJSON), object, (.=),Value)
import  Database.PostgreSQL.Simple.FromRow

type AppAction a = SpockActionCtx () Connection AppSession AppState a

data AppState = EmptyState
data AppSession = EmptySession

data Envelope = Envelope { envId :: Int, envData :: Value } deriving Show

instance FromRow Envelope where
  fromRow = Envelope <$> field <*> field

instance ToJSON Envelope where
   toJSON (Envelope envA envB) = object [ "id" .= envA, "data" .= envB ]

main :: IO ()
main =
  do pool<-createPool (connect (ConnectInfo "localhost" 5432 "" "" "envelopes") ) close 1 10 10
     spockCfg <- defaultSpockCfg EmptySession (PCPool pool) EmptyState
     runSpock 8080 (spock spockCfg app)

app :: SpockM Connection AppSession AppState ()
app = do 
    get root $
      text "Hello World!"
    get "json" $ do
      xs<-runQuery $ \conn -> 
        query_ conn  "select id,data from envelope where id = 1"
      json (xs::[Envelope])

然后我尝试使用 lambda 函数传递信封 ID,为此我还需要将 PostgreSQL-Simple query_ 更改为 query:

    get ( "json" <//> var  ) $ \eid -> do
      xs<-runQuery $ \conn -> 
        query conn  "select id,data from envelope where id = ?" (eid :: Int)
      json (xs::[Envelope])

我得到的错误是:

 No instance for (ToRow Int) arising from a use of ‘query’
   In the expression:
      query conn "select id,data from envelope where id = ?" (eid :: Int)
    In the second argument of ‘($)’, namely
      ‘\ conn
         -> query
              conn "select id,data from envelope where id = ?" (eid :: Int)’
    In a stmt of a 'do' block:
      xs <- runQuery
            $ \ conn
                -> query
                     conn "select id,data from envelope where id = ?" (eid :: Int)

即使没有 lambda 函数,我也有问题 return 只是查询的第一项。

可以在 bitbucket

上找到完整的源代码

我希望有人有时间在这里帮助我。 感谢您阅读。

错误基本上是说您不能将 Int 作为第三个参数传递给 queryquery 期望具有类型类 ToRow and Int is not one. What you might want to do in your case considering you only want to pass one value to query is to use Only 实例的内容。所以该行变为:

query conn  "select id,data from envelope where id = ?" (Only (eid :: Int))

根据soupis help我也在Postgres-Simple文档中找到了相应的部分。为了完成,文档提到了另一种语法,使用单例列表。因此,您可以简单地使用方括号而不是使用普通括号,它也可以工作:

query conn "select id,data from envelope where id = ?" [eid :: Int]

此外,特别是在我的示例中,仅 return 结果的第一行更有意义,结果证明使用 head 函数很简单。对于其他需要它的人,请按以下方式操作:

Spock 和 Prelude 都包含一个 head 函数。为了避免冲突,我决定隐藏 Spocks 函数,因为我无论如何都不会使用它。

在顶部添加到您的脚本:

import Web.Spock hiding(head)

然后将get部分改为:

get ( "json" <//> var  ) $ \eid -> do
      xs<-runQuery $ \conn -> 
        query conn  "select id,data from envelope where id = ?" [eid :: Int]
      json $ head (xs::[Envelope])

完成。