Safe Haskell | None |
---|---|
Language | Haskell2010 |
Selda is not LINQ, but they're definitely related.
Selda is a high-level EDSL for interacting with relational databases.
All database computations are performed within some monad implementing
the MonadSelda
type class. The SeldaT
monad over any MonadIO
is the
only pre-defined instance of MonadSelda
.
SeldaM
is provided as a convenient short-hand for SeldaT IO
.
To actually execute a database computation, you need one of the database
backends: selda-sqlite
or selda-postgresql
.
All Selda functions may throw SeldaError
when something goes wrong.
This includes database connection errors, uniqueness constraint errors,
etc.
The following example shows off Selda's most basic features -- creating, populating, modifying and querying tables -- and is intended to act as a Hello World-ish quickstart.
{-# LANGUAGE TypeOperators, OverloadedStrings #-} import Data.Text (Text, unpack) import Database.Selda import Database.Selda.SQLite people :: Table (Text :*: Int :*: Maybe Text) (people, pName :*: pAge :*: pPet) = tableWithSelectors "people" $ primary "name" :*: required "age" :*: optional "pet" main = withSQLite "people.sqlite" $ do createTable people insert_ people [ "Velvet" :*: 19 :*: Nothing , "Kobayashi" :*: 23 :*: Just "dragon" , "Miyu" :*: 10 :*: Nothing ] update_ people (\person -> person ! pName .== "Velvet") (\person -> person `with` [pPet := just "orthros"]) adults <- query $ do person <- select people restrict (person ! pAge .> 20) return (person ! pName :*: person ! pAge) n <- deleteFrom people (\person -> isNull (person ! pPet)) liftIO $ do putStrLn "The adults in the room are:" mapM_ printPerson adults putStrLn $ show n ++ " people were deleted for having no pets." printPerson :: Text :*: Int -> IO () printPerson (name :*: age) = putStrLn $ unpack name ++ ", age " ++ show age
Please see http://hackage.haskell.org/package/selda/#readme for a more comprehensive tutorial.
- class Monad m => MonadIO m where
- class MonadIO m => MonadSelda m
- data SeldaError
- data ValidationError
- data SeldaT m a
- type SeldaM = SeldaT IO
- data Table a
- data Query s a
- data Col s a
- class Typeable (Res r) => Result r where
- type Res r
- query :: (MonadSelda m, Result a) => Query s a -> m [Res a]
- transaction :: (MonadSelda m, MonadThrow m, MonadCatch m) => m a -> m a
- setLocalCache :: MonadIO m => Int -> m ()
- data Selector t a
- (!) :: forall s t a. ToDyn (Cols () t) => Cols s t -> Selector t a -> Col s a
- data Assignment s t where
- (:=) :: Selector t a -> Col s a -> Assignment s t
- with :: forall s t. ToDyn (Cols () t) => Cols s t -> [Assignment s t] -> Cols s t
- class SqlType a
- data Text :: *
- type family Cols s a where ...
- class Columns a
- data Order
- data a :*: b where
- select :: Columns (Cols s a) => Table a -> Query s (Cols s a)
- selectValues :: (Insert a, Columns (Cols s a)) => [a] -> Query s (Cols s a)
- restrict :: Col s Bool -> Query s ()
- limit :: Int -> Int -> Query (Inner s) a -> Query s a
- order :: Col s a -> Order -> Query s ()
- ascending :: Order
- descending :: Order
- (.==) :: SqlType a => Col s a -> Col s a -> Col s Bool
- (./=) :: SqlType a => Col s a -> Col s a -> Col s Bool
- (.>) :: SqlType a => Col s a -> Col s a -> Col s Bool
- (.<) :: SqlType a => Col s a -> Col s a -> Col s Bool
- (.>=) :: SqlType a => Col s a -> Col s a -> Col s Bool
- (.<=) :: SqlType a => Col s a -> Col s a -> Col s Bool
- like :: Col s Text -> Col s Text -> Col s Bool
- (.&&) :: Col s Bool -> Col s Bool -> Col s Bool
- (.||) :: Col s Bool -> Col s Bool -> Col s Bool
- not_ :: Col s Bool -> Col s Bool
- literal :: SqlType a => a -> Col s a
- int :: Int -> Col s Int
- float :: Double -> Col s Double
- text :: Text -> Col s Text
- true :: Col s Bool
- false :: Col s Bool
- null_ :: SqlType a => Col s (Maybe a)
- roundTo :: Col s Int -> Col s Double -> Col s Double
- length_ :: Col s Text -> Col s Int
- isNull :: Col s (Maybe a) -> Col s Bool
- round_ :: forall s a. (Typeable a, SqlType a, Num a) => Col s Double -> Col s a
- just :: SqlType a => Col s a -> Col s (Maybe a)
- fromBool :: (SqlType a, Num a) => Col s Bool -> Col s a
- fromInt :: (SqlType a, Num a) => Col s Int -> Col s a
- toString :: Col s a -> Col s Text
- data Aggr s a
- class Aggregates a
- type family OuterCols a where ...
- type family LeftCols a where ...
- data Inner s
- class SqlType a => MinMax a
- inner :: (Columns a, Columns (OuterCols a)) => (OuterCols a -> Col s Bool) -> Query (Inner s) a -> Query s (OuterCols a)
- suchThat :: (Columns a, Columns (OuterCols a)) => (OuterCols a -> Col s Bool) -> Query (Inner s) a -> Query s (OuterCols a)
- leftJoin :: (Columns a, Columns (OuterCols a), Columns (LeftCols a)) => (OuterCols a -> Col s Bool) -> Query (Inner s) a -> Query s (LeftCols a)
- aggregate :: (Columns (OuterCols a), Aggregates a) => Query (Inner s) a -> Query s (OuterCols a)
- groupBy :: Col (Inner s) a -> Query (Inner s) (Aggr (Inner s) a)
- count :: SqlType a => Col s a -> Aggr s Int
- avg :: (SqlType a, Num a) => Col s a -> Aggr s a
- sum_ :: (SqlType a, Num a) => Col s a -> Aggr s a
- max_ :: MinMax a => Col s a -> Aggr s a
- min_ :: MinMax a => Col s a -> Aggr s a
- class Insert a
- insert :: (MonadSelda m, Insert a) => Table a -> [a] -> m Int
- insert_ :: (MonadSelda m, Insert a) => Table a -> [a] -> m ()
- insertWithPK :: (MonadSelda m, Insert a) => Table a -> [a] -> m Int
- def :: SqlType a => a
- update :: (MonadSelda m, Columns (Cols s a), Result (Cols s a)) => Table a -> (Cols s a -> Col s Bool) -> (Cols s a -> Cols s a) -> m Int
- update_ :: (MonadSelda m, Columns (Cols s a), Result (Cols s a)) => Table a -> (Cols s a -> Col s Bool) -> (Cols s a -> Cols s a) -> m ()
- deleteFrom :: (MonadSelda m, Columns (Cols s a)) => Table a -> (Cols s a -> Col s Bool) -> m Int
- deleteFrom_ :: (MonadSelda m, Columns (Cols s a)) => Table a -> (Cols s a -> Col s Bool) -> m ()
- class TableSpec a
- type family ColSpecs a where ...
- data ColSpec a
- data TableName
- data ColName
- class SqlType a => NonNull a
- type family IsNullable a where ...
- data Nullable
- data NotNullable
- class Append a b where
- type family a :++: b where ...
- type family Selectors t a where ...
- class HasSelectors t a
- table :: forall a. TableSpec a => TableName -> ColSpecs a -> Table a
- tableWithSelectors :: forall a. (TableSpec a, HasSelectors a a) => TableName -> ColSpecs a -> (Table a, Selectors a a)
- selectors :: forall a. HasSelectors a a => Table a -> Selectors a a
- required :: NonNull a => ColName -> ColSpec a
- optional :: NonNull a => ColName -> ColSpec (Maybe a)
- primary :: NonNull a => ColName -> ColSpec a
- autoPrimary :: ColName -> ColSpec Int
- fk :: ColSpec a -> (Table t, Selector t a) -> ColSpec a
- unique :: SqlType a => ColSpec a -> ColSpec a
- createTable :: MonadSelda m => Table a -> m ()
- tryCreateTable :: MonadSelda m => Table a -> m ()
- dropTable :: MonadSelda m => Table a -> m ()
- tryDropTable :: MonadSelda m => Table a -> m ()
- data OnError
- compile :: Result a => Query s a -> (Text, [Param])
- compileCreateTable :: (Text -> [ColAttr] -> Maybe Text) -> OnError -> Table a -> Text
- compileDropTable :: OnError -> Table a -> Text
- compileInsert :: Insert a => Text -> Table a -> [a] -> (Text, [Param])
- compileUpdate :: forall s a. (Columns (Cols s a), Result (Cols s a)) => (Text -> Text) -> Table a -> (Cols s a -> Cols s a) -> (Cols s a -> Col s Bool) -> (Text, [Param])
- class Tup a
- type family Head a where ...
- first :: Tup a => a -> Head a
- second :: Tup b => (a :*: b) -> Head b
- third :: Tup c => (a :*: (b :*: c)) -> Head c
- fourth :: Tup d => (a :*: (b :*: (c :*: d))) -> Head d
- fifth :: Tup e => (a :*: (b :*: (c :*: (d :*: e)))) -> Head e
- sixth :: Tup f => (a :*: (b :*: (c :*: (d :*: (e :*: f))))) -> Head f
- seventh :: Tup g => (a :*: (b :*: (c :*: (d :*: (e :*: (f :*: g)))))) -> Head g
- eighth :: Tup h => (a :*: (b :*: (c :*: (d :*: (e :*: (f :*: (g :*: h))))))) -> Head h
- ninth :: Tup i => (a :*: (b :*: (c :*: (d :*: (e :*: (f :*: (h :*: (h :*: i)))))))) -> Head i
- tenth :: Tup j => (a :*: (b :*: (c :*: (d :*: (e :*: (f :*: (g :*: (h :*: (i :*: j))))))))) -> Head j
Running queries
class Monad m => MonadIO m where #
Monads in which IO
computations may be embedded.
Any monad built by applying a sequence of monad transformers to the
IO
monad will be an instance of this class.
Instances should satisfy the following laws, which state that liftIO
is a transformer of monads:
MonadIO IO | |
MonadIO m => MonadIO (ListT m) | |
MonadIO m => MonadIO (MaybeT m) | |
MonadIO m => MonadIO (SeldaT m) # | |
(Error e, MonadIO m) => MonadIO (ErrorT e m) | |
MonadIO m => MonadIO (ExceptT e m) | |
MonadIO m => MonadIO (StateT s m) | |
MonadIO m => MonadIO (StateT s m) | |
(Monoid w, MonadIO m) => MonadIO (WriterT w m) | |
(Monoid w, MonadIO m) => MonadIO (WriterT w m) | |
MonadIO m => MonadIO (IdentityT * m) | |
MonadIO m => MonadIO (ContT * r m) | |
MonadIO m => MonadIO (ReaderT * r m) | |
(Monoid w, MonadIO m) => MonadIO (RWST r w s m) | |
(Monoid w, MonadIO m) => MonadIO (RWST r w s m) | |
class MonadIO m => MonadSelda m Source #
Some monad with Selda SQL capabilitites.
MonadIO m => MonadSelda (SeldaT m) Source # | |
data SeldaError Source #
Thrown by any function in SeldaT
if an error occurs.
data ValidationError Source #
An error occurred when validating a database table. If this error is thrown, there is a bug in your database schema, and the particular table that triggered the error is unusable. Since validation is deterministic, this error will be thrown on every consecutive operation over the offending table.
Therefore, it is not meaningful to handle this exception in any way, just fix your bug instead.
Monad transformer adding Selda SQL capabilities.
MonadTrans SeldaT Source # | |
Monad m => Monad (SeldaT m) Source # | |
Functor m => Functor (SeldaT m) Source # | |
Monad m => Applicative (SeldaT m) Source # | |
MonadIO m => MonadIO (SeldaT m) Source # | |
MonadThrow m => MonadThrow (SeldaT m) Source # | |
MonadCatch m => MonadCatch (SeldaT m) Source # | |
MonadMask m => MonadMask (SeldaT m) Source # | |
MonadIO m => MonadSelda (SeldaT m) Source # | |
A database table.
Tables are parameterized over their column types. For instance, a table
containing one string and one integer, in that order, would have the type
Table (Text :*: Int)
, and a table containing only a single string column
would have the type Table Text
.
Table and column names may contain any character except NUL
, and be
non-empty. Column names must be unique per table.
An SQL query.
A database column. A column is often a literal column table, but can also be an expression over such a column or a constant expression.
Columns b => Columns ((:*:) (Col k s a) b) Source # | |
(Typeable * a, SqlType a, Result b) => Result ((:*:) (Col * s a) b) Source # | |
Fractional (Col k s (Maybe Int)) Source # | |
Fractional (Col k s Int) Source # | |
Fractional (Col k s (Maybe Double)) Source # | |
Fractional (Col k s Double) Source # | |
(SqlType a, Num a) => Num (Col k s (Maybe a)) Source # | |
(SqlType a, Num a) => Num (Col k s a) Source # | |
IsString (Col k s Text) Source # | |
Columns (Col k s a) Source # | |
(Typeable * a, SqlType a) => Result (Col * s a) Source # | |
type Res ((:*:) (Col * s a) b) Source # | |
type Res (Col * s a) Source # | |
class Typeable (Res r) => Result r Source #
An acceptable query result type; one or more columns stitched together
with :*:
.
toRes, finalCols
query :: (MonadSelda m, Result a) => Query s a -> m [Res a] Source #
Run a query within a Selda monad. In practice, this is often a SeldaT
transformer on top of some other monad.
Selda transformers are entered using backend-specific withX
functions,
such as withSQLite
from the SQLite backend.
transaction :: (MonadSelda m, MonadThrow m, MonadCatch m) => m a -> m a Source #
Perform the given computation atomically. If an exception is raised during its execution, the enture transaction will be rolled back, and the exception re-thrown.
setLocalCache :: MonadIO m => Int -> m () Source #
Set the maximum local cache size to n
. A cache size of zero disables
local cache altogether. Changing the cache size will also flush all
entries. Note that the cache is shared among all Selda computations running
within the same process.
By default, local caching is turned off.
WARNING: local caching is guaranteed to be consistent with the underlying database, ONLY under the assumption that no other process will modify it. Also note that the cache is shared between ALL Selda computations running within the same process.
Constructing queries
(!) :: forall s t a. ToDyn (Cols () t) => Cols s t -> Selector t a -> Col s a Source #
Get the value at the given index from the given inductive tuple.
data Assignment s t where Source #
A selector-value assignment pair.
(:=) :: Selector t a -> Col s a -> Assignment s t infixl 2 |
with :: forall s t. ToDyn (Cols () t) => Cols s t -> [Assignment s t] -> Cols s t Source #
For each selector-value pair in the given list, on the given tuple, update the field pointed out by the selector with the corresponding value.
Any datatype representable in (Selda's subset of) SQL.
mkLit, sqlType, fromSql, defaultValue
A space efficient, packed, unboxed Unicode text type.
Any column tuple.
toTup, fromTup
The order in which to sort result rows.
data a :*: b where infixr 1 Source #
An inductively defined "tuple", or heterogeneous, non-empty list.
(Typeable * a, HasSelectors t b) => HasSelectors t ((:*:) a b) Source # | |
(Eq a, Eq b) => Eq ((:*:) a b) Source # | |
(Ord a, Ord b) => Ord ((:*:) a b) Source # | |
(Show a, Show b) => Show ((:*:) a b) Source # | |
Generic ((:*:) a b) Source # | |
Tup ((:*:) a b) Source # | |
TableSpec b => TableSpec ((:*:) a b) Source # | |
Columns b => Columns ((:*:) (Col k s a) b) Source # | |
Aggregates b => Aggregates ((:*:) (Aggr (Inner s) a) b) Source # | |
(SqlType a, Insert b) => Insert ((:*:) a b) Source # | |
(Typeable * a, SqlType a, Result b) => Result ((:*:) (Col * s a) b) Source # | |
Append b c => Append ((:*:) a b) c Source # | |
type Rep ((:*:) a b) Source # | |
type Res ((:*:) (Col * s a) b) Source # | |
select :: Columns (Cols s a) => Table a -> Query s (Cols s a) Source #
Query the given table. Result is returned as an inductive tuple, i.e.
first :*: second :*: third <- query tableOfThree
.
selectValues :: (Insert a, Columns (Cols s a)) => [a] -> Query s (Cols s a) Source #
Query an ad hoc table of type a
. Each element in the given list represents
one row in the ad hoc table.
restrict :: Col s Bool -> Query s () Source #
Restrict the query somehow. Roughly equivalent to WHERE
.
limit :: Int -> Int -> Query (Inner s) a -> Query s a Source #
Drop the first m
rows, then get at most n
of the remaining rows from the
given subquery.
order :: Col s a -> Order -> Query s () Source #
Sort the result rows in ascending or descending order on the given row.
descending :: Order Source #
Ordering for order
.
Expressions over columns
like :: Col s Text -> Col s Text -> Col s Bool infixl 4 Source #
The SQL LIKE
operator; matches strings with %
wildcards.
For instance:
"%gon" `like` "dragon" .== true
roundTo :: Col s Int -> Col s Double -> Col s Double Source #
Round a column to the given number of decimals places.
Converting between column types
round_ :: forall s a. (Typeable a, SqlType a, Num a) => Col s Double -> Col s a Source #
Round a value to the nearest integer. Equivalent to roundTo 0
.
just :: SqlType a => Col s a -> Col s (Maybe a) Source #
Lift a non-nullable column to a nullable one. Useful for creating expressions over optional columns:
people :: Table (Text :*: Int :*: Maybe Text) people = table "people" $ required "name" ¤ required "age" ¤ optional "pet" peopleWithCats = do name :*: _ :*: pet <- select people restrict (pet .== just "cat") return name
fromBool :: (SqlType a, Num a) => Col s Bool -> Col s a Source #
Convert a boolean column to any numeric type.
fromInt :: (SqlType a, Num a) => Col s Int -> Col s a Source #
Convert an integer column to any numeric type.
Inner queries
A single aggregate column.
Aggregate columns may not be used to restrict queries.
When returned from an aggregate
subquery, an aggregate column is
converted into a non-aggregate column.
Aggregates b => Aggregates ((:*:) (Aggr (Inner s) a) b) Source # | |
Aggregates (Aggr (Inner s) a) Source # | |
class Aggregates a Source #
One or more aggregate columns.
unAggrs
Aggregates b => Aggregates ((:*:) (Aggr (Inner s) a) b) Source # | |
Aggregates (Aggr (Inner s) a) Source # | |
type family OuterCols a where ... Source #
Convert one or more inner column to equivalent columns in the outer query.
OuterCols (Aggr (Inner s) a :*: Aggr (Inner s) b) = Col s a :*: Col s b
,
for instance.
type family LeftCols a where ... Source #
The results of a left join are always nullable, as there is no guarantee
that all joined columns will be non-null.
JoinCols a
where a
is an extensible tuple is that same tuple, but in
the outer query and with all elements nullable.
For instance:
LeftCols (Col (Inner s) Int :*: Col (Inner s) Text) = Col s (Maybe Int) :*: Col s (Maybe Text)
Denotes an inner query.
For aggregation, treating sequencing as the cartesian product of queries
does not work well.
Instead, we treat the sequencing of aggregate
with other
queries as the cartesian product of the aggregated result of the query,
a small but important difference.
However, for this to work, the aggregate query must not depend on any
columns in the outer product. Therefore, we let the aggregate query be
parameterized over Inner s
if the parent query is parameterized over s
,
to enforce this separation.
Aggregates b => Aggregates ((:*:) (Aggr (Inner s) a) b) Source # | |
Aggregates (Aggr (Inner s) a) Source # | |
:: (Columns a, Columns (OuterCols a)) | |
=> (OuterCols a -> Col s Bool) | Predicate determining which lines to join. | Right-hand query to join. |
-> Query (Inner s) a | |
-> Query s (OuterCols a) |
Perform an INNER JOIN
with the current result set and the given query.
suchThat :: (Columns a, Columns (OuterCols a)) => (OuterCols a -> Col s Bool) -> Query (Inner s) a -> Query s (OuterCols a) Source #
Synonym for inner
for infix use:
person <- select people home <- select homes `suchThat` \home -> home ! owner .== person ! name return (person ! name :*: home ! isApartment)
:: (Columns a, Columns (OuterCols a), Columns (LeftCols a)) | |
=> (OuterCols a -> Col s Bool) | Predicate determining which lines to join. | Right-hand query to join. |
-> Query (Inner s) a | |
-> Query s (LeftCols a) |
Perform a LEFT JOIN
with the current result set (i.e. the outer query)
as the left hand side, and the given query as the right hand side.
Like with aggregate
, the inner (or right) query must not depend on the
outer (or right) one.
The given predicate over the values returned by the inner query determines for each row whether to join or not. This predicate may depend on any values from the outer query.
For instance, the following will list everyone in the people
table
together with their address if they have one; if they don't, the address
field will be NULL
.
getAddresses :: Query s (Col s Text :*: Col s (Maybe Text)) getAddresses = do name :*: _ <- select people _ :*: address <- leftJoin (\(n :*: _) -> n .== name) (select addresses) return (name :*: address)
aggregate :: (Columns (OuterCols a), Aggregates a) => Query (Inner s) a -> Query s (OuterCols a) Source #
Execute a query, returning an aggregation of its results.
The query must return an inductive tuple of Aggregate
columns.
When aggregate
returns, those columns are converted into non-aggregate
columns, which may then be used to further restrict the query.
Note that aggregate queries must not depend on outer queries, nor must they return any non-aggregate columns. Attempting to do either results in a type error.
The SQL HAVING
keyword can be implemented by combining aggregate
and restrict
:
-- Find the number of people living on every address, for all addresses -- with more than one tenant: -- SELECT COUNT(name) AS c, address FROM housing GROUP BY name HAVING c > 1 numPpl = do num_tenants :*: address <- aggregate $ do _ :*: address <- select housing groupBy address return (count address :*: some address) restrict (num_tenants .> 1) return (num_tenants :*: address)
groupBy :: Col (Inner s) a -> Query (Inner s) (Aggr (Inner s) a) Source #
Group an aggregate query by a column. Attempting to group a non-aggregate query is a type error. An aggregate representing the grouped-by column is returned, which can be returned from the aggregate query. For instance, if you want to find out how many people have a pet at home:
aggregate $ do name :*: pet_name <- select people name' <- groupBy name return (name' :*: count(pet_name) > 0)
count :: SqlType a => Col s a -> Aggr s Int Source #
The number of non-null values in the given column.
avg :: (SqlType a, Num a) => Col s a -> Aggr s a Source #
The average of all values in the given column.
max_ :: MinMax a => Col s a -> Aggr s a Source #
The greatest value in the given column. Texts are compared lexically.
min_ :: MinMax a => Col s a -> Aggr s a Source #
The smallest value in the given column. Texts are compared lexically.
Modifying tables
An inductive tuple of Haskell-level values (i.e. Int :*: Maybe Text
)
which can be inserted into a table.
params
insert :: (MonadSelda m, Insert a) => Table a -> [a] -> m Int Source #
Insert the given values into the given table. All columns of the table
must be present. If your table has an auto-incrementing primary key,
use the special value def
for that column to get the auto-incrementing
behavior.
Returns the number of rows that were inserted.
To insert a list of tuples into a table with auto-incrementing primary key:
people :: Table (Int :*: Text :*: Int :*: Maybe Text) people = table "ppl" $ autoPrimary "id" ¤ required "name" ¤ required "age" ¤ optional "pet" main = withSQLite "my_database.sqlite" $ do insert_ people [ def :*: "Link" :*: 125 :*: Just "horse" , def :*: "Zelda" :*: 119 :*: Nothing , ... ]
insert_ :: (MonadSelda m, Insert a) => Table a -> [a] -> m () Source #
Like insert
, but does not return anything.
Use this when you really don't care about how many rows were inserted.
insertWithPK :: (MonadSelda m, Insert a) => Table a -> [a] -> m Int Source #
Like insert
, but returns the primary key of the last inserted row.
Attempting to run this operation on a table without an auto-incrementing
primary key is a type error.
def :: SqlType a => a Source #
The default value for a column during insertion. For an auto-incrementing primary key, the default value is the next key.
Using def
in any other context than insertion results in a runtime error.
Likewise, if def
is given for a column that does not have a default
value, the insertion will fail.
:: (MonadSelda m, Columns (Cols s a), Result (Cols s a)) | |
=> Table a | The table to update. |
-> (Cols s a -> Col s Bool) | Predicate. |
-> (Cols s a -> Cols s a) | Update function. |
-> m Int |
Update the given table using the given update function, for all rows matching the given predicate. Returns the number of updated rows.
update_ :: (MonadSelda m, Columns (Cols s a), Result (Cols s a)) => Table a -> (Cols s a -> Col s Bool) -> (Cols s a -> Cols s a) -> m () Source #
Like update
, but doesn't return the number of updated rows.
deleteFrom :: (MonadSelda m, Columns (Cols s a)) => Table a -> (Cols s a -> Col s Bool) -> m Int Source #
From the given table, delete all rows matching the given predicate. Returns the number of deleted rows.
deleteFrom_ :: (MonadSelda m, Columns (Cols s a)) => Table a -> (Cols s a -> Col s Bool) -> m () Source #
Like deleteFrom
, but does not return the number of deleted rows.
Defining schemas
An inductive tuple forming a table specification.
mergeSpecs
type family ColSpecs a where ... Source #
An inductive tuple where each element is a column specification.
Name of a database table.
Name of a database column.
class SqlType a => NonNull a Source #
Any SQL type which is NOT nullable.
(SqlType a, (~) * (IsNullable a) NotNullable) => NonNull a Source # | |
type family IsNullable a where ... Source #
Is the given type nullable?
IsNullable (Maybe a) = Nullable | |
IsNullable a = NotNullable |
Used by IsNullable
to indicate a nullable type.
data NotNullable Source #
Used by IsNullable
to indicate a nullable type.
type family a :++: b where ... Source #
Normalized append of two inductive tuples. Note that this will flatten any nested inductive tuples.
class HasSelectors t a Source #
Any table type that can have selectors generated.
mkSel
(~) * (Selectors t a) (Selector t a) => HasSelectors t a Source # | |
(Typeable * a, HasSelectors t b) => HasSelectors t ((:*:) a b) Source # | |
table :: forall a. TableSpec a => TableName -> ColSpecs a -> Table a Source #
A table with the given name and columns.
tableWithSelectors :: forall a. (TableSpec a, HasSelectors a a) => TableName -> ColSpecs a -> (Table a, Selectors a a) Source #
A pair of the table with the given name and columns, and all its selectors. For example:
tbl :: Table (Int :*: Text) (tbl, tblBar :*: tblBaz) = tableWithSelectors "foo" $ required "bar" :*: required "baz" q :: Query s Text q = tblBaz <$> select tbl
selectors :: forall a. HasSelectors a a => Table a -> Selectors a a Source #
Generate selector functions for the given table. Selectors can be used to access the fields of a query result tuple, avoiding the need to pattern match on the entire tuple.
tbl :: Table (Int :*: Text) tbl = table "foo" $ required "bar" :*: required "baz" (tblBar :*: tblBaz) = selectors tbl q :: Query s Text q = tblBaz <$> select tbl
optional :: NonNull a => ColName -> ColSpec (Maybe a) Source #
A nullable column with the given name.
primary :: NonNull a => ColName -> ColSpec a Source #
Marks the given column as the table's primary key.
A table may only have one primary key; marking more than one key as
primary will result in ValidationError
during validation.
autoPrimary :: ColName -> ColSpec Int Source #
Automatically increment the given attribute if not specified during insert.
Also adds the PRIMARY KEY
and UNIQUE
attributes on the column.
fk :: ColSpec a -> (Table t, Selector t a) -> ColSpec a Source #
Add a foreign key constraint to the given column, referencing
the column indicated by the given table and selector.
If the referenced column is not a primary key or has a
uniqueness constraint, a ValidationError
will be thrown
during validation.
unique :: SqlType a => ColSpec a -> ColSpec a Source #
Add a uniqueness constraint to the given column. Adding a uniqueness constraint to a column that is already implied to be unique, such as a primary key, is a no-op.
Creating and dropping tables
createTable :: MonadSelda m => Table a -> m () Source #
Create a table from the given schema.
tryCreateTable :: MonadSelda m => Table a -> m () Source #
Create a table from the given schema, unless it already exists.
dropTable :: MonadSelda m => Table a -> m () Source #
Drop the given table.
tryDropTable :: MonadSelda m => Table a -> m () Source #
Drop the given table, if it exists.
Compiling and inspecting queries
compile :: Result a => Query s a -> (Text, [Param]) Source #
Compile a query into a parameterised SQL statement.
The types given are tailored for SQLite. To translate SQLite types into
whichever types are used by your backend, use compileWith
.
compileCreateTable :: (Text -> [ColAttr] -> Maybe Text) -> OnError -> Table a -> Text Source #
Compile a CREATE TABLE
query from a table definition.
compileInsert :: Insert a => Text -> Table a -> [a] -> (Text, [Param]) Source #
Compile an INSERT
query, given the keyword representing default values
in the target SQL dialect, a table and a list of items corresponding
to the table.
:: (Columns (Cols s a), Result (Cols s a)) | |
=> (Text -> Text) | Type translation function. |
-> Table a | The table to update. |
-> (Cols s a -> Cols s a) | Update function. |
-> (Cols s a -> Col s Bool) | Predicate: update only when true. |
-> (Text, [Param]) |
Compile an UPDATE
query.
Tuple convenience functions
fourth :: Tup d => (a :*: (b :*: (c :*: d))) -> Head d Source #
Get the fourth element of an inductive tuple.
fifth :: Tup e => (a :*: (b :*: (c :*: (d :*: e)))) -> Head e Source #
Get the fifth element of an inductive tuple.
sixth :: Tup f => (a :*: (b :*: (c :*: (d :*: (e :*: f))))) -> Head f Source #
Get the sixth element of an inductive tuple.
seventh :: Tup g => (a :*: (b :*: (c :*: (d :*: (e :*: (f :*: g)))))) -> Head g Source #
Get the seventh element of an inductive tuple.
eighth :: Tup h => (a :*: (b :*: (c :*: (d :*: (e :*: (f :*: (g :*: h))))))) -> Head h Source #
Get the eighth element of an inductive tuple.