-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Type-level Relational DB combinators. -- -- Ribbit is yet another type safe relational database library for -- Haskell, heavily inspired by the amazing Servant library. The goal is -- to create a type-level language for defining table schemas "as a -- type", queries that operate on those schemas, and, tangentially, -- "backends" that can do something useful with those types like talk to -- an actual database. @package ribbit @version 1.0.0.0 -- | "postgresql-simple"-backed query ribbit implementation. module Database.Ribbit.PostgreSQL -- | Given a Haskell type, produce the PostgreSQL type of columns that -- store values of that haskell type. class HasPsqlType a psqlType :: HasPsqlType a => proxy a -> PsqlType -- | Represents the "base" PostgreSQL type. We say "base" type because -- whether the type is nullable is handle automatically. -- -- e.g. -- -- newtype PsqlType PsqlType :: Text -> PsqlType [unPsqlType] :: PsqlType -> Text -- | Execute a statement. execute :: forall m query. (MonadIO m, ToRow (ArgsType query), KnownSymbol (Render query)) => Connection -> Proxy query -> ArgsType query -> m Int64 -- | Execute a query against a PostgreSQL database connection. query :: forall m query. (MonadIO m, KnownSymbol (Render query), ToRow (ArgsType query), FromRow (ResultType query)) => Connection -> Proxy query -> ArgsType query -> m [ResultType query] -- | Create the indicated table in the database. -- -- See createTableStatement for details. createTable :: forall proxy1 proxy2 key table m. (KnownSymbol (Name table), HasPsqlTypes (DBSchema table), HasFields (DBSchema table), HasFields key, IsSubset key (DBSchema table) ~ 'True, MonadIO m) => Connection -> proxy1 key -> proxy2 table -> m () -- | Produce the statement used to create a table. -- -- In this example, we create an employee table with a multi-part primary -- key, one nullable field, and a few non-nullable fields. -- --
--   data Employee
--   instance Table Employee where
--     type Name = "employees"
--     type DBSchema =
--       Field "company_id" Int
--       :> Field "id" Int
--       :> Field "name" Text
--       :> Field "quit_date" (Maybe Day)
--   
--   let
--     primaryKey :: Proxy '["company_id", "id"]
--     primaryKey = Proxy
--     
--     table :: Proxy Employee
--     table = Proxy
--   
--   in
--     createTableStatement primaryKey table
--   
-- -- This will produce the statement: -- --
--   "create table employees (company_id integer not null, id integer not null, name text not null, quit_date date, primary key (company_id, id));"
--   
createTableStatement :: forall proxy1 proxy2 table key. (KnownSymbol (Name table), HasPsqlTypes (DBSchema table), HasFields (DBSchema table), HasFields key, IsSubset key (DBSchema table) ~ 'True) => proxy1 key -> proxy2 table -> Text -- | Produce a list of field names from a schema. class HasFields a class HasPsqlTypes a -- | Figure out if a Haskell type is "nullable" in sql. class HasIsNullable a -- | Make sure the fields in the list are actually part of the schema. type family IsSubset fields schema -- | Like FromRow, but defined here so we can avoid orphaned -- instances. class FromRow a -- | Like ToRow, but defined here to avoid orphan instances. class ToRow a instance Data.String.IsString Database.Ribbit.PostgreSQL.PsqlType instance forall k1 k2 (typ :: k2) (name :: k1). (Database.Ribbit.PostgreSQL.HasIsNullable typ, Database.Ribbit.PostgreSQL.HasPsqlType typ) => Database.Ribbit.PostgreSQL.HasPsqlTypes (Database.Ribbit.Table.Field name typ) instance forall k1 k2 (typ :: k2) more (name :: k1). (Database.Ribbit.PostgreSQL.HasIsNullable typ, Database.Ribbit.PostgreSQL.HasPsqlType typ, Database.Ribbit.PostgreSQL.HasPsqlTypes more) => Database.Ribbit.PostgreSQL.HasPsqlTypes (Database.Ribbit.Table.Field name typ Database.Ribbit.Table.:> more) instance Database.Ribbit.PostgreSQL.HasPsqlType a => Database.Ribbit.PostgreSQL.HasPsqlType (GHC.Maybe.Maybe a) instance Database.Ribbit.PostgreSQL.HasPsqlType Data.Text.Internal.Text instance Database.Ribbit.PostgreSQL.HasPsqlType GHC.Types.Int instance Database.Ribbit.PostgreSQL.HasPsqlType Data.Time.Calendar.Days.Day instance Database.Ribbit.PostgreSQL.HasIsNullable (GHC.Maybe.Maybe a) instance forall k (a :: k). Database.Ribbit.PostgreSQL.HasIsNullable a instance forall k (name :: GHC.Types.Symbol) (typ :: k). GHC.TypeLits.KnownSymbol name => Database.Ribbit.PostgreSQL.HasFields (Database.Ribbit.Table.Field name typ) instance forall k (name :: GHC.Types.Symbol) more (typ :: k). (GHC.TypeLits.KnownSymbol name, Database.Ribbit.PostgreSQL.HasFields more) => Database.Ribbit.PostgreSQL.HasFields (Database.Ribbit.Table.Field name typ Database.Ribbit.Table.:> more) instance Database.Ribbit.PostgreSQL.HasFields '[] instance (GHC.TypeLits.KnownSymbol name, Database.Ribbit.PostgreSQL.HasFields more) => Database.Ribbit.PostgreSQL.HasFields (name : more) instance Database.Ribbit.PostgreSQL.FromRow a => Database.PostgreSQL.Simple.FromRow.FromRow (Database.Ribbit.PostgreSQL.Wrap a) instance Database.Ribbit.PostgreSQL.ToRow a => Database.PostgreSQL.Simple.ToRow.ToRow (Database.Ribbit.PostgreSQL.Wrap a) instance (Database.Ribbit.PostgreSQL.FromRow a, Database.Ribbit.PostgreSQL.FromRow b) => Database.Ribbit.PostgreSQL.FromRow (a Database.Ribbit.Table.:> b) instance Database.PostgreSQL.Simple.FromField.FromField a => Database.Ribbit.PostgreSQL.FromRow (Data.Tuple.Only.Only a) instance (Database.Ribbit.PostgreSQL.ToRow a, Database.Ribbit.PostgreSQL.ToRow b) => Database.Ribbit.PostgreSQL.ToRow (a Database.Ribbit.Table.:> b) instance Database.PostgreSQL.Simple.ToField.ToField a => Database.Ribbit.PostgreSQL.ToRow (Data.Tuple.Only.Only a) instance Database.Ribbit.PostgreSQL.ToRow () -- | This module attepts to define a type-level language for describing -- database shcemas (i.e. schemas "as a type"), and the queries that -- operate on them in such a way that: -- -- 1) The schema and/or query is completely defined at the type level -- (sans runtime arguments to query parameters). -- -- 2) The meaning of a schema and/or query is immediately obvious to -- anyone who knows SQL, and -- -- 3) The schema and/or query can be extended, deconstructed, or -- interpreted by third parties for their own purposes. -- -- To that end, each schema is a new type, defined by you, using the -- constructors provided by this library. The same goes for queries. Each -- query is a separate type defined with constructors from this library. -- -- We provide a PostgreSQL backend so that real work can be accomplished, -- but if the backend is missing something you need, then the idea is -- that you can use your own type families and type classes to extend the -- schema and query languages, or interpret them differently for your own -- needs including writing entirely new backends if need be. module Database.Ribbit -- | Type class for defining your own tables. The primary way for you to -- introduce a new schema is to instantiate this type class for one of -- your types. -- -- E.g.: -- --
--   data MyTable
--   instance Table MyTable where
--     type Name MyTable = "my_table"
--     type DBSchema MyTable =
--       Field "id" Int
--       :> Field "my_non_nullable_text_field" Text
--       :> Field "my_nullable_int_field" (Maybe Int)
--   
class Table relation where { type family Name relation :: Symbol; type family DBSchema relation; } -- | Define a field in a database schema, where: -- -- -- -- E.g: -- -- data Field name typ -- | String two types together. Int :> Int -- :> Int is similar in principal to the nested tuple -- (Int, (Int, Int)), but looks a whole lot nicer -- when the number of elements becomes large. -- -- This is how you build up a schema from a collection of Field -- types. -- -- E.g.: -- --
--   Field "foo" Int
--   :> Field "bar" Text
--   :> Field "baz" (Maybe Text)
--   
-- -- It also the mechanism by which this library builds up the Haskell -- types for query parameters and resulting rows that get returned. So if -- you have a query that accepts three text query parameters, that type -- represented in Haskell is going to be (Only Text -- :> Only Text :> Only -- Text). -- -- If that query returns rows that contain a Text, an Int, and a Text, -- then the type of the rows will be (Only Text -- :> Only Int :> Only -- Text). data a :> b (:>) :: a -> b -> (:>) a b infixr 5 :> infixr 5 :> -- | SELECT constructor, used for starting a SELECT -- statement. data Select fields -- | FROM constructor, used for attaching a SELECT projection to a -- relation in the database. data From proj relation infixl 6 `From` -- | AS constructor, used for attaching a name to a table in a FROM -- clause. data As relation (name :: Symbol) infix 9 `As` -- | ON keyword, for joins. data On join (conditions :: *) infix 7 `On` -- | Left Joins. data LeftJoin left right infix 8 `LeftJoin` -- | Insert statement. data InsertInto table fields -- | Delete statement. data DeleteFrom table -- | Update statement. data Update table (fields :: [Symbol]) -- | WHERE constructor, used for attaching conditions to a query. data Where query conditions infixl 6 `Where` -- | "=" constructor for conditions. data Equals (l :: k1) (r :: k2) infix 9 `Equals` -- | "!=" constructor for conditions. data NotEquals l r infix 9 `NotEquals` -- | "<" constructor for conditions. data Lt l r infix 9 `Lt` -- | "<=" constructor for conditions. data Lte l r infix 9 `Lte` -- | ">" constructor for conditions. data Gt l r infix 9 `Gt` -- | ">=" constructor for conditions. data Gte l r infix 9 `Gte` -- | AND constructor for conditions. data And l r infixr 8 `And` -- | OR constructor for conditions. data Or (l :: k1) (r :: k2) infixr 7 `Or` -- | NOT conditional constructor. data Not a -- | Is a field null? data IsNull (field :: Symbol) -- | Is a field not null? data NotNull (field :: Symbol) -- | "?" constructor, used to indicate the presence of a query parameter. data (?) -- | Produce the type represeting the placeholder ("?") values in a -- paramaterized query. type family ArgsType query -- | Produce the type of rows return by a query. type family ResultType query -- | Render a query. type family Render a :: Symbol