项目作者: nikita-volkov

项目描述 :
Performant PostgreSQL driver with a flexible mapping API
高级语言: Haskell
项目地址: git://github.com/nikita-volkov/hasql.git
创建时间: 2014-10-27T13:13:02Z
项目社区:https://github.com/nikita-volkov/hasql

开源协议:MIT License

下载


Fast PostgreSQL driver for Haskell with a flexible mapping API

Hasql is a highly efficient PostgreSQL driver for Haskell with a typesafe yet flexible mapping API. It targets both the users, who need maximum control, and the users who face the typical tasks of DB-powered applications, providing them with higher-level APIs. Currently it is known to be the fastest driver in the Haskell ecosystem.

[!IMPORTANT]
Hasql is one of the supported targets of the pGenie code generator, which empowers it with schema and query validation, and relieves you from boilerplate.

Status

Hackage

Hasql is production-ready, actively maintained and the API is pretty stable. It’s used by many companies and most notably by the Postgrest project.

Ecosystem

Hasql is not just a single library, it is a granular ecosystem of composable libraries, each isolated to perform its own task and stay simple.

Benefits of being an ecosystem

  • Simplicity. Each library in isolation provides a simple API, which is hopefully easier to comprehend.

  • Flexibility and composability. The user picks and chooses the features, thus precisely matching the level of abstraction that he needs for his task.

  • Much more stable and more descriptive semantic versioning. E.g., a change in the API of the “hasql-transaction” library won’t affect any of the other libraries and it gives the user a more precise information about which part of his application he needs to update to conform.

  • Interchangeability and competition of the ecosystem components. E.g., not everyone will agree with the restrictive design decisions made in the “hasql-transaction” library. However those decisions are not imposed on the user, and instead of having endless debates about how to abstract over transactions, another extension library can simply be released, which will provide a different interpretation of what the abstraction over transactions should be.

  • Horizontal scalability of the ecosystem. Instead of posting feature- or pull-requests, the users are encouraged to release their own small extension-libraries, with themselves becoming the copyright owners and taking on the maintenance responsibilities. Compare this model to the classical one, where some core-team is responsible for everything. One is scalable, the other is not.

Tutorials

Videos

There’s several videos on Hasql done as part of a nice intro-level series of live Haskell+Bazel coding by the “Ants Are Everywhere” YouTube channel:

Articles

Short Example

Following is a complete application, which performs some arithmetic in Postgres using Hasql.

  1. {-# LANGUAGE OverloadedStrings, QuasiQuotes #-}
  2. import Prelude
  3. import Data.Int
  4. import Data.Functor.Contravariant
  5. import Hasql.Session (Session)
  6. import Hasql.Statement (Statement(..))
  7. import qualified Hasql.Session as Session
  8. import qualified Hasql.Decoders as Decoders
  9. import qualified Hasql.Encoders as Encoders
  10. import qualified Hasql.Connection as Connection
  11. main :: IO ()
  12. main = do
  13. Right connection <- Connection.acquire connectionSettings
  14. result <- Session.run (sumAndDivModSession 3 8 3) connection
  15. print result
  16. where
  17. connectionSettings = Connection.connectionString "localhost" 5432 "postgres" "" "postgres"
  18. -- * Sessions
  19. --
  20. -- Session is an abstraction over the database connection and all possible errors.
  21. -- It is used to execute statements.
  22. -- It is composable and has a Monad instance.
  23. --
  24. -- It's recommended to define sessions in a dedicated 'Sessions'
  25. -- submodule of your project.
  26. -------------------------
  27. sumAndDivModSession :: Int64 -> Int64 -> Int64 -> Session (Int64, Int64)
  28. sumAndDivModSession a b c = do
  29. -- Get the sum of a and b
  30. sumOfAAndB <- Session.statement (a, b) sumStatement
  31. -- Divide the sum by c and get the modulo as well
  32. Session.statement (sumOfAAndB, c) divModStatement
  33. -- * Statements
  34. --
  35. -- Statement is a definition of an individual SQL-statement,
  36. -- accompanied by a specification of how to encode its parameters and
  37. -- decode its result.
  38. --
  39. -- It's recommended to define statements in a dedicated 'Statements'
  40. -- submodule of your project.
  41. -------------------------
  42. sumStatement :: Statement (Int64, Int64) Int64
  43. sumStatement = Statement sql encoder decoder True where
  44. sql = "select $1 + $2"
  45. encoder =
  46. (fst >$< Encoders.param (Encoders.nonNullable Encoders.int8)) <>
  47. (snd >$< Encoders.param (Encoders.nonNullable Encoders.int8))
  48. decoder = Decoders.singleRow (Decoders.column (Decoders.nonNullable Decoders.int8))
  49. divModStatement :: Statement (Int64, Int64) (Int64, Int64)
  50. divModStatement = Statement sql encoder decoder True where
  51. sql = "select $1 / $2, $1 % $2"
  52. encoder =
  53. (fst >$< Encoders.param (Encoders.nonNullable Encoders.int8)) <>
  54. (snd >$< Encoders.param (Encoders.nonNullable Encoders.int8))
  55. decoder = Decoders.singleRow row where
  56. row =
  57. (,) <$>
  58. Decoders.column (Decoders.nonNullable Decoders.int8) <*>
  59. Decoders.column (Decoders.nonNullable Decoders.int8)

For the general use-case it is advised to prefer declaring statements using the “hasql-th” library, which validates the statements at compile-time and generates codecs automatically. So the above two statements could be implemented the following way:

  1. import qualified Hasql.TH as TH -- from "hasql-th"
  2. sumStatement :: Statement (Int64, Int64) Int64
  3. sumStatement =
  4. [TH.singletonStatement|
  5. select ($1 :: int8 + $2 :: int8) :: int8
  6. |]
  7. divModStatement :: Statement (Int64, Int64) (Int64, Int64)
  8. divModStatement =
  9. [TH.singletonStatement|
  10. select
  11. (($1 :: int8) / ($2 :: int8)) :: int8,
  12. (($1 :: int8) % ($2 :: int8)) :: int8
  13. |]