packages feed

ribbit 1.0.0.1 → 1.1.0.0

raw patch · 10 files changed

+176/−175 lines, 10 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Database.Ribbit.PostgreSQL: execute :: forall m query. (MonadIO m, ToRow (ArgsType query), KnownSymbol (Render query)) => Connection -> Proxy query -> ArgsType query -> m Int64
+ Database.Ribbit.PostgreSQL: execute :: forall m query. (MonadIO m, ToRow (ParamsType query), KnownSymbol (Render query)) => Connection -> Proxy query -> ParamsType query -> m Int64
- Database.Ribbit.PostgreSQL: query :: forall m query. (MonadIO m, KnownSymbol (Render query), ToRow (ArgsType query), FromRow (ResultType query)) => Connection -> Proxy query -> ArgsType query -> m [ResultType query]
+ Database.Ribbit.PostgreSQL: query :: forall m query. (MonadIO m, KnownSymbol (Render query), ToRow (ParamsType query), FromRow (ResultType query)) => Connection -> Proxy query -> ParamsType query -> m [ResultType query]

Files

ribbit.cabal view
@@ -1,6 +1,6 @@  name:                ribbit-version:             1.0.0.1+version:             1.1.0.0 synopsis:            Type-level Relational DB combinators. description:         Ribbit is yet another type safe relational database                      library for Haskell, heavily inspired by the@@ -25,10 +25,10 @@     Database.Ribbit     Database.Ribbit.PostgreSQL   other-modules:       -    Database.Ribbit.Args     Database.Ribbit.Conditions     Database.Ribbit.Delete     Database.Ribbit.Insert+    Database.Ribbit.Params     Database.Ribbit.Render     Database.Ribbit.Select     Database.Ribbit.Table
src/Database/Ribbit.hs view
@@ -1,9 +1,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-}@@ -201,14 +199,15 @@     Produce the Haskell type corresponding to the query parameters for a select     statement: -    > ArgsType Query ~ Only Int -- Our statement has only one parameter, which is an int.+    > ParamsType Query ~ Only Int -- Our statement has only one parameter, which is an int.      Produce the Haskell type corresponding to the rows produced by the query:      > ResultType Query ~ Only Text -- Our query procudes only one column, a text.    -}-  ArgsType,+  ParamsType,+  ParamsTypeSchema,   ResultType,   Render,   -- ValidField,@@ -220,11 +219,11 @@ ) where  -import Database.Ribbit.Args (ArgsType, ResultType) import Database.Ribbit.Conditions (Where, Equals, NotEquals, Lt, Lte,   Gt, Gte, And, Or, Not, IsNull, NotNull, type (?)) import Database.Ribbit.Delete (DeleteFrom) import Database.Ribbit.Insert (InsertInto)+import Database.Ribbit.Params (ParamsType, ResultType, ParamsTypeSchema) import Database.Ribbit.Render (Render) import Database.Ribbit.Select (Select, From, As, On, LeftJoin) import Database.Ribbit.Table (Table(Name, DBSchema), Field, (:>)((:>)))@@ -307,42 +306,42 @@ -- >      forall m query. -- >      ( MonadIO m -- >      , KnownSymbol (Render query)--- >      , ToRow (ArgsType query)+-- >      , ToRow (ParamsType query) -- >      , FromRow (ResultType query) -- >      ) -- >   => Connection -- >   -> Proxy query--- >   -> ArgsType query+-- >   -> ParamsType query -- >   -> m [ResultType query] -- -- In particular, I want to point how how, in addition to the query--- itself, 'Database.Ribbit.PostgreSQL.query' accepts an @'ArgsType' query@+-- itself, 'Database.Ribbit.PostgreSQL.query' accepts an @'ParamsType' query@ -- as a parameter, and produces a list of @'ResultType' query@ items. ----- 'ArgsType' and 'ResultType' are where the type safety magic+-- 'ParamsType' and 'ResultType' are where the type safety magic -- happens. They are type families that, given a query type, produce the -- haskell type of the arguments to that query, and the rows produce by -- that query, respectively: ----- * 'ArgsType' - Given a query, produce the type of the embedded query+-- * 'ParamsType' - Given a query, produce the type of the embedded query --   parameters. -- * 'ResultType' - Given a query, produce the type of rows produced by --   that query. -- --- +-------------------------+------------------------------------------------+------------------------------------------------------------------------------+--- | Example                 | Resulting type                                 | Reason                                                                       |--- +=========================+================================================+==============================================================================+--- | @'ArgsType' MyQuery@    | @'Only' 'Text'@                                | Our query on only has one parameter, which is compared against the company   |--- |                         |                                                | "name" field, indicating it must be of type 'Text', because that is what     |--- |                         |                                                | we have defined the "name" column to be in our database schema using:        |--- |                         |                                                |                                                                              |--- |                         |                                                | @Field "name" Text@                                                          |--- |                         |                                                |                                                                              |--- +-------------------------+------------------------------------------------+------------------------------------------------------------------------------+--- | @'ResultType' MyQuery@  | @'Only' 'Text' ':>' 'Only' ('Maybe' 'Int')@    |  The Fields that 'MyQuery' is selecting are employee "name" and              |--- |                         |                                                |  "salary". Name is a non-null Text, and salary is a nullable integer.        |--- |                         |                                                |                                                                              |--- +-------------------------+------------------------------------------------+------------------------------------------------------------------------------++-- +-----------------------------+-----------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------++-- | Example                     | Resulting type                                                                                                  | Reason                                                                       |+-- +=============================+=================================================================================================================+==============================================================================++-- | @'ParamsType' MyQuery@ | @'Data.Tuple.Only.Only' 'Data.Text.Text'@                                                                  | Our query on only has one parameter, which is compared against the company   |+-- |                             |                                                                                                                 | "name" field, indicating it must be of type 'Text', because that is what     |+-- |                             |                                                                                                                 | we have defined the "name" column to be in our database schema using:        |+-- |                             |                                                                                                                 |                                                                              |+-- |                             |                                                                                                                 | @Field "name" Text@                                                          |+-- |                             |                                                                                                                 |                                                                              |+-- +-----------------------------+-----------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------++-- | @'ResultType' MyQuery@ | @'Data.Tuple.Only.Only' 'Data.Text.Text' ':>' 'Data.Tuple.Only.Only' ('Maybe' 'Int')@  | The Fields that 'MyQuery' is selecting are employee "name" and               |+-- |                             |                                                                                                                 | "salary". Name is a non-null Text, and salary is a nullable integer.         |+-- |                             |                                                                                                                 |                                                                              |+-- +-----------------------------+-----------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------+ -- -- Therefore, we can invoke the query thusly: -- 
− src/Database/Ribbit/Args.hs
@@ -1,114 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE UndecidableInstances #-}--{- | Translate statement parameters into Haskell types. -}-module Database.Ribbit.Args (-  ArgsType,-  ResultType,-) where---import Data.Tuple.Only (Only)-import Data.Type.Bool (If)-import Database.Ribbit.Conditions (Where, Equals, NotEquals, Lt, Lte,-  Gt, Gte, Not, And, Or, type (?))-import Database.Ribbit.Delete (DeleteFrom)-import Database.Ribbit.Insert (InsertInto)-import Database.Ribbit.Select (From, Select)-import Database.Ribbit.Table (DBSchema, Flatten, (:>), Field, ValidField,-  NotInSchema)-import Database.Ribbit.Update (Update)-import GHC.TypeLits (TypeError, ErrorMessage(ShowType, (:$$:)))-import qualified GHC.TypeLits as Lit---{- |-  Produce the type represeting the placeholder ("?") values in a-  paramaterized query.--}-type family ArgsType query where-  ArgsType (_ `From` relation `Where` conditions) =-    ArgsType (DBSchema relation, conditions)--  ArgsType (InsertInto relation fields) =-    ProjectionType fields (DBSchema relation)--  ArgsType (DeleteFrom relation `Where` conditions) =-    ArgsType (DBSchema relation, conditions)--  ArgsType (Update relation fields) =-    ProjectionType fields (DBSchema relation)-  ArgsType (Update relation fields `Where` conditions) =-    ProjectionType fields (DBSchema relation)-    :> ArgsType (DBSchema relation, conditions)--  ArgsType (schema, And a b) =-    StripUnit (Flatten (ArgsType (schema, a) :> ArgsType (schema, b)))-  ArgsType (schema, Or a b) =-    StripUnit (Flatten (ArgsType (schema, a) :> ArgsType (schema, b)))-  ArgsType (schema, Condition field (?)) =-    ProjectionType '[field] schema-  ArgsType (schema, Condition (?) field) =-    ProjectionType '[field] schema-  ArgsType (schema, Condition l r) =-    If-      (ValidField r schema)-      (If (ValidField l schema) () (NotInSchema l schema))-      (NotInSchema r schema)-  ArgsType (schema, Equals l r) = ArgsType (schema, Condition l r)-  ArgsType (schema, NotEquals l r) = ArgsType (schema, Condition l r)-  ArgsType (schema, Lt l r) = ArgsType (schema, Condition l r)-  ArgsType (schema, Lte l r) = ArgsType (schema, Condition l r)-  ArgsType (schema, Gt l r) = ArgsType (schema, Condition l r)-  ArgsType (schema, Gte l r) = ArgsType (schema, Condition l r)-  ArgsType (schema, Not a) = ArgsType (schema, a)-  ArgsType _ = ()---{- |-  Helper for 'ArgsType'. Reduces the number of equations required, because-  'ArgsType' doesn't actually care about which conditionl operator it-  is inspecting.--}-data Condition l r---{- |-  Strip redundant unit types out of a string of types. This is used-  mainly to help simplify the implementation of 'ArgsType'.--}-type family StripUnit a where-  StripUnit (() :> a) = StripUnit a-  StripUnit (a :> ()) = StripUnit a-  StripUnit (a :> b) = a :> StripUnit b-  StripUnit a = a---type family ProjectionType proj schema where-  ProjectionType '[name] schema =-    LookupType name schema schema-  ProjectionType (name:more) schema =-    LookupType name schema schema-    :> ProjectionType more schema---{- | Produce the type of rows return by a query. -}-type family ResultType query where-  ResultType (Select fields `From` relation) =-    ProjectionType fields (DBSchema relation)-  ResultType (query `Where` conditions) = ResultType query-  ResultType query =-    TypeError ('Lit.Text "Malformed Query" ':$$: 'ShowType query)---type family LookupType name schema context where-  LookupType name (Field name typ) _ = Only typ-  LookupType name (Field name typ :> _) _ = Only typ-  LookupType name (Field _ typ) context = NotInSchema name context-  LookupType name (_ :> more) context = LookupType name more context-  LookupType name a context = NotInSchema name context--
src/Database/Ribbit/Conditions.hs view
@@ -25,7 +25,11 @@ ) where  -import Database.Ribbit.Table (Validate)+import Data.Type.Bool (If)+import Database.Ribbit.Params (ParamsTypeSchema, ResultType,+  ProjectionType)+import Database.Ribbit.Table (Validate, Flatten, (:>), ValidField,+  NotInSchema) import GHC.TypeLits (Symbol, AppendSymbol, TypeError, ErrorMessage((:<>:),   ShowType)) import qualified GHC.TypeLits as Lit@@ -202,5 +206,47 @@  {- | "?" constructor, used to indicate the presence of a query parameter. -} data (?)+++type instance ParamsTypeSchema schema (And a b) =+  StripUnit (Flatten (ParamsTypeSchema schema a :> ParamsTypeSchema schema b))+type instance ParamsTypeSchema schema (Or a b) =+  StripUnit (Flatten (ParamsTypeSchema schema a :> ParamsTypeSchema schema b))+type instance ParamsTypeSchema schema (Equals l r) = CompParams schema (Comparison l r)+type instance ParamsTypeSchema schema (NotEquals l r) = CompParams schema (Comparison l r)+type instance ParamsTypeSchema schema (Lt l r) = CompParams schema (Comparison l r)+type instance ParamsTypeSchema schema (Lte l r) = CompParams schema (Comparison l r)+type instance ParamsTypeSchema schema (Gt l r) = CompParams schema (Comparison l r)+type instance ParamsTypeSchema schema (Gte l r) = CompParams schema (Comparison l r)+type instance ParamsTypeSchema schema (Not a) = ParamsTypeSchema schema a+++type instance ResultType (query `Where` conditions) = ResultType query+++{- | Produce the parameters for a comparison operator. -}+type family CompParams schema comp where+  CompParams schema (Comparison field (?)) = ProjectionType '[field] schema+  CompParams schema (Comparison (?) field) = ProjectionType '[field] schema+  CompParams schema (Comparison l r) =+    If+      (ValidField r schema)+      (If (ValidField l schema) () (NotInSchema l schema))+      (NotInSchema r schema)+++{- | Helper for 'CompParams'. -}+data Comparison l r+++{- |+  Strip redundant unit types out of a string of types. This is used+  mainly to help simplify the implementation of 'ParamsType'.+-}+type family StripUnit a where+  StripUnit (() :> a) = StripUnit a+  StripUnit (a :> ()) = StripUnit a+  StripUnit (a :> b) = a :> StripUnit b+  StripUnit a = a  
src/Database/Ribbit/Delete.hs view
@@ -11,6 +11,7 @@   import Database.Ribbit.Conditions (RenderConditions, Where)+import Database.Ribbit.Params (ParamsType, ParamsTypeSchema) import Database.Ribbit.Render (Render) import Database.Ribbit.Table (Name, DBSchema) import GHC.TypeLits (AppendSymbol)@@ -28,5 +29,12 @@   Render (DeleteFrom table)   `AppendSymbol` " WHERE "   `AppendSymbol` RenderConditions conditions (DBSchema table)+++type instance ParamsType (DeleteFrom relation `Where` conditions) =+  ParamsTypeSchema (DBSchema relation) conditions+++type instance ParamsType (DeleteFrom relation) = ()  
src/Database/Ribbit/Insert.hs view
@@ -11,8 +11,9 @@ ) where  +import Database.Ribbit.Params (ParamsType, ProjectionType) import Database.Ribbit.Render (Render)-import Database.Ribbit.Table (Name)+import Database.Ribbit.Table (Name, DBSchema) import GHC.TypeLits (AppendSymbol)  @@ -44,4 +45,7 @@     `AppendSymbol` ", "     `AppendSymbol` RendPlaceholderList (f2:more) ++type instance ParamsType (InsertInto relation fields) =+  ProjectionType fields (DBSchema relation) 
+ src/Database/Ribbit/Params.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++{- | Translate statement parameters into Haskell types. -}+module Database.Ribbit.Params (+  ParamsType,+  ResultType,+  ParamsTypeSchema,+  ProjectionType,+) where+++import Data.Tuple.Only (Only)+import Database.Ribbit.Table ((:>), Field, NotInSchema)+++{- |+  Produce the type represeting the placeholder ("?") values in a+  paramaterized query.++  This type family is open and extendable.+-}+type family ParamsType query+++{- | Produce the type of rows return by a query. -}+type family ResultType query+++{- |+  Produce the parameters type in relation to a particiular schema.++  This type family is open and extendable.+-}+type family ParamsTypeSchema schema a+++type family ProjectionType proj schema where+  ProjectionType '[name] schema =+    LookupType name schema schema+  ProjectionType (name:more) schema =+    LookupType name schema schema+    :> ProjectionType more schema+++type family LookupType name schema context where+  LookupType name (Field name typ) _ = Only typ+  LookupType name (Field name typ :> _) _ = Only typ+  LookupType name (Field _ typ) context = NotInSchema name context+  LookupType name (_ :> more) context = LookupType name more context+  LookupType name a context = NotInSchema name context++
src/Database/Ribbit/PostgreSQL.hs view
@@ -51,7 +51,7 @@ import Database.PostgreSQL.Simple (Connection) import Database.PostgreSQL.Simple.FromField (FromField) import Database.PostgreSQL.Simple.ToField (Action, ToField)-import Database.Ribbit.Args (ArgsType, ResultType)+import Database.Ribbit.Params (ParamsType, ResultType) import Database.Ribbit.Render (Render) import Database.Ribbit.Table ((:>)((:>)), Name, DBSchema, Field,   ValidField)@@ -67,10 +67,10 @@ {- | Execute a statement. -} execute ::      forall m query.-     (MonadIO m, ToRow (ArgsType query), KnownSymbol (Render query))+     (MonadIO m, ToRow (ParamsType query), KnownSymbol (Render query))   => Connection   -> Proxy query-  -> ArgsType query+  -> ParamsType query   -> m Int64 execute conn _theQuery args =   liftIO $@@ -300,12 +300,12 @@      forall m query.      ( MonadIO m      , KnownSymbol (Render query)-     , ToRow (ArgsType query)+     , ToRow (ParamsType query)      , FromRow (ResultType query)      )   => Connection   -> Proxy query-  -> ArgsType query+  -> ParamsType query   -> m [ResultType query] query conn _theQuery args =   liftIO . (fmap . fmap) unWrap $
src/Database/Ribbit/Select.hs view
@@ -21,10 +21,13 @@ ) where  -import Database.Ribbit.Conditions (RenderJoinConditions, Where, RenderConditions)+import Database.Ribbit.Conditions (RenderJoinConditions, Where,+  RenderConditions)+import Database.Ribbit.Params (ParamsType, ResultType, ParamsTypeSchema,+  ProjectionType) import Database.Ribbit.Render (Render)-import Database.Ribbit.Table (Name, DBSchema, Field, (:>), Table, Flatten,-  Validate)+import Database.Ribbit.Table (Name, DBSchema, Field, (:>), Table,+  Flatten, Validate) import GHC.TypeLits (AppendSymbol, Symbol)  @@ -173,5 +176,16 @@   Render (proj `From` table)   `AppendSymbol` " WHERE "   `AppendSymbol` RenderConditions conditions (DBSchema table)+++type instance ParamsType (_ `From` relation `Where` conditions) =+  ParamsTypeSchema (DBSchema relation) conditions+++type instance ParamsType (Select proj `From` relation) = ()+++type instance ResultType (Select fields `From` relation) =+    ProjectionType fields (DBSchema relation)  
src/Database/Ribbit/Update.hs view
@@ -12,8 +12,10 @@   import Database.Ribbit.Conditions (RenderConditions, Where)+import Database.Ribbit.Params (ParamsType, ParamsTypeSchema,+  ProjectionType) import Database.Ribbit.Render (Render)-import Database.Ribbit.Table (Name, DBSchema)+import Database.Ribbit.Table (Name, DBSchema, (:>)) import GHC.TypeLits (AppendSymbol, Symbol)  @@ -41,27 +43,13 @@     `AppendSymbol` " = ?, "     `AppendSymbol` RenderUpdateFields more --- {- UPDATE -}--- instance (KnownSymbol (Name table), RenderUpdates updates)---   =>---     Render (Update table updates)---   where---     render _proxy =---       "UPDATE "---       <> symbolVal (Proxy @(Name table))---       <> " SET "---       <> renderUpdates (Proxy @updates)--- --- --- {- | Render the updates to a table. -}--- class RenderUpdates a where---   renderUpdates :: proxy a -> Text--- instance (KnownSymbol field) => RenderUpdates '[field] where---   renderUpdates _ = symbolVal (Proxy @field) <> " = ?"--- instance (KnownSymbol field, RenderUpdates (m:ore))---   =>---     RenderUpdates (field : (m:ore))---   where---     renderUpdates _ =---       symbolVal (Proxy @field) <> " = ?, "---       <> renderUpdates (Proxy @(m:ore))++type instance ParamsType (Update relation fields) =+  ProjectionType fields (DBSchema relation)+++type instance ParamsType (Update relation fields `Where` conditions) =+  ProjectionType fields (DBSchema relation)+  :> ParamsTypeSchema (DBSchema relation) conditions++