From f5905512ff9eef5e9209aadfcec51fb201b0fa01 Mon Sep 17 00:00:00 2001 From: Veronika Romashkina Date: Thu, 11 Jul 2019 16:26:17 +0800 Subject: [PATCH] [#5] Write some README (#14) * [#5] Write some README Resolves #5 * Update src/PgNamed.hs Co-Authored-By: Dmitrii Kovanikov * Update README.md Co-Authored-By: Dmitrii Kovanikov * Update README.md Co-Authored-By: Dmitrii Kovanikov * Update README.md Co-Authored-By: Dmitrii Kovanikov * Fix after review --- README.md | 67 +++++++++++++++++++++++++++++++++-- postgresql-simple-named.cabal | 6 ++-- src/PgNamed.hs | 32 +++++++++-------- 3 files changed, 85 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 93dcbce..ddbc510 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,70 @@ # postgresql-simple-named +[![Build status](https://img.shields.io/travis/Holmusk/postgresql-simple-named.svg?logo=travis)](https://travis-ci.org/Holmusk/postgresql-simple-named) [![Hackage](https://img.shields.io/hackage/v/postgresql-simple-named.svg?logo=haskell)](https://hackage.haskell.org/package/postgresql-simple-named) -[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](LICENSE) [![Stackage Lts](http://stackage.org/package/postgresql-simple-named/badge/lts)](http://stackage.org/lts/package/postgresql-simple-named) [![Stackage Nightly](http://stackage.org/package/postgresql-simple-named/badge/nightly)](http://stackage.org/nightly/package/postgresql-simple-named) -[![Build status](https://img.shields.io/travis/Holmusk/postgresql-simple-named.svg?logo=travis)](https://travis-ci.org/Holmusk/postgresql-simple-named) +[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](LICENSE) + +This library introduces the implementation of named parameters for the +[`postgresql-simple`][pgs] library. `postgresql-simple-named` is considered to +be used along with the [`postgresql-simple`][pgs] library, so you could refer +there for the original documentation of primary functions. This package solves +exclusively one particular problem — gives the ability to use named parameters +instead of `?` in quasi-quoter queries and offers essential functions for substituting +variables in queries (`queryNamed`, `executeNamed`). + +## Example + +Operator `=?` binds named parameters with the corresponding values. Named +parameters inside SQL query start with the '?' character and can contain +lowercase and uppercase letters, digits and underscore. Below you can find a +basic example of how query with named parameters could look like: + +```haskell +queryNamed dbConnection [sql| + SELECT + id, name, city + FROM users + WHERE name = ?nameParam + AND age = ?ageParam -Implementation of named parameters for `postgresql-simple` library +|] [ "nameParam" =? "John" + , "ageParam" =? 42 + ] +``` + +This feature can be extremely helpful when the query uses some parameters more than once: + +```haskell +query dbConnection [sql| + SELECT + col1, col2 + FROM my_table + WHERE id = ? + AND (? IS NULL OR id > ? ) + AND (? IS NULL OR id < ? ) + +|] (someId, minId, minId, maxId, maxId) +``` + +This is how the query looks like with the `postgresql-simple` library. You can +rewrite it the following way using the `postgresql-simple-named` library: + +```haskell +queryNamed dbConnection [sql| + SELECT + col1, col2 + FROM my_table + WHERE id = ?someId + AND (?minId IS NULL OR id > ?minId ) + AND (?maxId IS NULL OR id < ?maxId ) + +|] [ "someId" =? 42 + , "minId" =? 1 + , "maxId" =? 100 + ] +``` ## How to build @@ -19,3 +77,6 @@ Build the library with either `cabal new-build` or `stack build`. docker run -p 5432\:5432 -e POSTGRES_USER=postgres -e POSTGRES_DB=pg_named postgres\:10.5-alpine ``` * Run tests using `cabal new-test` or `stack test` + + +[pgs]: https://hackage.haskell.org/package/postgresql-simple diff --git a/postgresql-simple-named.cabal b/postgresql-simple-named.cabal index 92ec0ff..cb476b4 100644 --- a/postgresql-simple-named.cabal +++ b/postgresql-simple-named.cabal @@ -3,7 +3,7 @@ name: postgresql-simple-named version: 0.0.0.0 synopsis: Implementation of named parameters for `postgresql-simple` library description: - Implementation of named parameters for `postgresql-simple` library. + Implementation of named parameters for @postgresql-simple@ library. . Here is an exaple of how it could be used in your code: . @@ -21,8 +21,8 @@ homepage: https://github.com/Holmusk/postgresql-simple-named bug-reports: https://github.com/Holmusk/postgresql-simple-named/issues license: MPL-2.0 license-file: LICENSE -author: Holmusk -maintainer: Holmusk +author: Dmitrii Kovanikov, Veronika Romashkina +maintainer: Holmusk copyright: 2019 Holmusk category: Database build-type: Simple diff --git a/src/PgNamed.hs b/src/PgNamed.hs index 5c82604..dfc780f 100644 --- a/src/PgNamed.hs +++ b/src/PgNamed.hs @@ -4,18 +4,19 @@ {- | Introduces named parameters for @postgresql-simple@ library. It uses @?@ question mark symbol as the indicator of the named parameter which -is replaced with the standard syntax with question marks. Check out the example -of usage: +is replaced with the standard syntax with question marks. + +Check out the example of usage: @ -queryNamed [sql| +'queryNamed' [sql| SELECT * FROM users WHERE foo = ?foo AND bar = ?bar AND baz = ?foo -|] [ "foo" =? "fooBar" - , "bar" =? "barVar" +|] [ "foo" '=?' "fooBar" + , "bar" '=?' "barVar" ] @ -} @@ -28,6 +29,7 @@ module PgNamed -- * Errors , PgNamedError (..) + , WithNamedError -- * Functions to deal with named parameters , extractNames @@ -71,7 +73,7 @@ data NamedParam = NamedParam data PgNamedError -- | Named parameter is not specified. = PgNamedParam Name - -- | Query has no names inside but was called with named functions, + -- | Query has no names inside but was called with named functions. | PgNoNames PG.Query -- | Query contains an empty name. | PgEmptyName PG.Query @@ -79,7 +81,7 @@ data PgNamedError -- | Type alias for monads that can throw errors of the 'PgNamedError' type. -type WithError = MonadError PgNamedError +type WithNamedError = MonadError PgNamedError instance Show PgNamedError where show e = "PostgreSQL named parameter error: " ++ case e of @@ -99,8 +101,8 @@ lookupName n = lookup n . map (\NamedParam{..} -> (namedParamName, namedParamPar SELECT name, user FROM users WHERE id = ?id @ -and returns either the error or query with all all names replaced by -questiosn marks @?@ with list of the names in the order of their appearance. +and returns either the error or the query with all names replaced by +question marks @?@ with the list of the names in the order of their appearance. For example: @@ -132,9 +134,11 @@ extractNames qr = go (PG.fromQuery qr) >>= \case isNameChar c = isAlphaNum c || c == '_' --- | Returns the list of values to use in query by given list of 'Name's. +{- | Returns the list of values to use in query by given list of 'Name's. +Throws 'PgNamedError' if any named parameter is not specified. +-} namesToRow - :: forall m . WithError m + :: forall m . WithNamedError m => NonEmpty Name -- ^ List of the names used in query -> [NamedParam] -- ^ List of the named parameters -> m (NonEmpty PG.Action) @@ -176,7 +180,7 @@ queryNamed dbConnection [sql| @ -} queryNamed - :: (MonadIO m, WithError m, PG.FromRow res) + :: (MonadIO m, WithNamedError m, PG.FromRow res) => PG.Connection -- ^ Database connection -> PG.Query -- ^ Query with named parameters inside -> [NamedParam] -- ^ The list of named parameters to be used in the query @@ -197,7 +201,7 @@ executeNamed dbConnection [sql| @ -} executeNamed - :: (MonadIO m, WithError m) + :: (MonadIO m, WithNamedError m) => PG.Connection -- ^ Database connection -> PG.Query -- ^ Query with named parameters inside -> [NamedParam] -- ^ The list of named parameters to be used in the query @@ -208,7 +212,7 @@ executeNamed conn qNamed params = -- | Helper to use named parameters. withNamedArgs - :: WithError m + :: WithNamedError m => PG.Query -> [NamedParam] -> m (PG.Query, NonEmpty PG.Action)