packages feed

selda 0.4.0.0 → 0.5.0.0

raw patch · 14 files changed

+115/−68 lines, 14 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Database.Selda: class s ~ t => Same s t
+ Database.Selda.Backend.Internal: instance Control.Monad.Trans.Class.MonadTrans (Database.Selda.Backend.Internal.SeldaT b)
+ Database.Selda.SqlType: instance GHC.Generics.Generic (Database.Selda.SqlType.ID a)
+ Database.Selda.SqlType: instance GHC.Generics.Generic Database.Selda.SqlType.RowID
- Database.Selda: (.&&) :: Col s Bool -> Col s Bool -> Col s Bool
+ Database.Selda: (.&&) :: Same s t => Col s Bool -> Col t Bool -> Col s Bool
- Database.Selda: (./=) :: SqlType a => Col s a -> Col s a -> Col s Bool
+ Database.Selda: (./=) :: (Same s t, SqlType a) => Col s a -> Col t a -> Col s Bool
- Database.Selda: (.<) :: SqlOrd a => Col s a -> Col s a -> Col s Bool
+ Database.Selda: (.<) :: (Same s t, SqlOrd a) => Col s a -> Col t a -> Col s Bool
- Database.Selda: (.<=) :: SqlOrd a => Col s a -> Col s a -> Col s Bool
+ Database.Selda: (.<=) :: (Same s t, SqlOrd a) => Col s a -> Col t a -> Col s Bool
- Database.Selda: (.==) :: SqlType a => Col s a -> Col s a -> Col s Bool
+ Database.Selda: (.==) :: (Same s t, SqlType a) => Col s a -> Col t a -> Col s Bool
- Database.Selda: (.>) :: SqlOrd a => Col s a -> Col s a -> Col s Bool
+ Database.Selda: (.>) :: (Same s t, SqlOrd a) => Col s a -> Col t a -> Col s Bool
- Database.Selda: (.>=) :: SqlOrd a => Col s a -> Col s a -> Col s Bool
+ Database.Selda: (.>=) :: (Same s t, SqlOrd a) => Col s a -> Col t a -> Col s Bool
- Database.Selda: (.||) :: Col s Bool -> Col s Bool -> Col s Bool
+ Database.Selda: (.||) :: Same s t => Col s Bool -> Col t Bool -> Col s Bool
- Database.Selda: groupBy :: SqlType a => Col (Inner s) a -> Query (Inner s) (Aggr (Inner s) a)
+ Database.Selda: groupBy :: (Same s t, SqlType a) => Col (Inner s) a -> Query (Inner t) (Aggr (Inner t) a)
- Database.Selda: ifNull :: SqlType a => Col s a -> Col s (Maybe a) -> Col s a
+ Database.Selda: ifNull :: (Same s t, SqlType a) => Col s a -> Col t (Maybe a) -> Col s a
- Database.Selda: ifThenElse :: SqlType a => Col s Bool -> Col s a -> Col s a -> Col s a
+ Database.Selda: ifThenElse :: (Same s t, Same t u, SqlType a) => Col s Bool -> Col t a -> Col u a -> Col s a
- Database.Selda: index :: Attribute Selector t c
+ Database.Selda: index :: Attribute Group t c
- Database.Selda: indexUsing :: IndexMethod -> Attribute Selector t c
+ Database.Selda: indexUsing :: IndexMethod -> Attribute Group t c
- Database.Selda: is :: SqlType c => Selector r c -> c -> Row s r -> Col s Bool
+ Database.Selda: is :: forall r s c. SqlType c => Selector r c -> c -> Row s r -> Col s Bool
- Database.Selda: isIn :: (Set set, SqlType a) => Col s a -> set (Col s a) -> Col s Bool
+ Database.Selda: isIn :: (Set set, Same s t, SqlType a) => Col s a -> set (Col t a) -> Col s Bool
- Database.Selda: like :: Col s Text -> Col s Text -> Col s Bool
+ Database.Selda: like :: Same s t => Col s Text -> Col t Text -> Col s Bool
- Database.Selda: limit :: Int -> Int -> Query (Inner s) a -> Query s (OuterCols a)
+ Database.Selda: limit :: Same s t => Int -> Int -> Query (Inner s) a -> Query t (OuterCols a)
- Database.Selda: matchNull :: (SqlType a, SqlType b) => Col s b -> (Col s a -> Col s b) -> Col s (Maybe a) -> Col s b
+ Database.Selda: matchNull :: (SqlType a, SqlType b, Same s t) => Col s b -> (Col s a -> Col s b) -> Col t (Maybe a) -> Col s b
- Database.Selda: order :: SqlType a => Col s a -> Order -> Query s ()
+ Database.Selda: order :: (Same s t, SqlType a) => Col s a -> Order -> Query t ()
- Database.Selda: restrict :: Col s Bool -> Query s ()
+ Database.Selda: restrict :: Same s t => Col s Bool -> Query t ()
- Database.Selda.Nullable: (?*) :: (a :?~ b, Num (NonNull a)) => Col s a -> Col s b -> Col s (Maybe (NonNull a))
+ Database.Selda.Nullable: (?*) :: (a :?~ b, Num (NonNull a), Same s t) => Col s a -> Col t b -> Col s (Maybe (NonNull a))
- Database.Selda.Nullable: (?+) :: (a :?~ b, Num (NonNull a)) => Col s a -> Col s b -> Col s (Maybe (NonNull a))
+ Database.Selda.Nullable: (?+) :: (a :?~ b, Num (NonNull a), Same s t) => Col s a -> Col t b -> Col s (Maybe (NonNull a))
- Database.Selda.Nullable: (?-) :: (a :?~ b, Num (NonNull a)) => Col s a -> Col s b -> Col s (Maybe (NonNull a))
+ Database.Selda.Nullable: (?-) :: (a :?~ b, Num (NonNull a), Same s t) => Col s a -> Col t b -> Col s (Maybe (NonNull a))
- Database.Selda.Nullable: (?/) :: (a :?~ b, Fractional (Col s (NonNull a))) => Col s a -> Col s b -> Col s (Maybe (NonNull a))
+ Database.Selda.Nullable: (?/) :: (a :?~ b, Fractional (Col s (NonNull a)), Same s t) => Col s a -> Col t b -> Col s (Maybe (NonNull a))
- Database.Selda.Nullable: (?/=) :: (a :?~ b, SqlType a) => Col s a -> Col s b -> Col s (Maybe Bool)
+ Database.Selda.Nullable: (?/=) :: (a :?~ b, SqlType a, Same s t) => Col s a -> Col t b -> Col s (Maybe Bool)
- Database.Selda.Nullable: (?<) :: (a :?~ b, SqlOrd (NonNull a)) => Col s a -> Col s b -> Col s (Maybe Bool)
+ Database.Selda.Nullable: (?<) :: (a :?~ b, SqlOrd (NonNull a), Same s t) => Col s a -> Col t b -> Col s (Maybe Bool)
- Database.Selda.Nullable: (?<=) :: (a :?~ b, SqlOrd (NonNull a)) => Col s a -> Col s b -> Col s (Maybe Bool)
+ Database.Selda.Nullable: (?<=) :: (a :?~ b, SqlOrd (NonNull a), Same s t) => Col s a -> Col t b -> Col s (Maybe Bool)
- Database.Selda.Nullable: (?==) :: (a :?~ b, SqlType a) => Col s a -> Col s b -> Col s (Maybe Bool)
+ Database.Selda.Nullable: (?==) :: (a :?~ b, SqlType a, Same s t) => Col s a -> Col t b -> Col s (Maybe Bool)
- Database.Selda.Nullable: (?>) :: (a :?~ b, SqlOrd (NonNull a)) => Col s a -> Col s b -> Col s (Maybe Bool)
+ Database.Selda.Nullable: (?>) :: (a :?~ b, SqlOrd (NonNull a), Same s t) => Col s a -> Col t b -> Col s (Maybe Bool)
- Database.Selda.Nullable: (?>=) :: (a :?~ b, SqlOrd (NonNull a)) => Col s a -> Col s b -> Col s (Maybe Bool)
+ Database.Selda.Nullable: (?>=) :: (a :?~ b, SqlOrd (NonNull a), Same s t) => Col s a -> Col t b -> Col s (Maybe Bool)
- Database.Selda.Nullable: nonNull :: SqlType a => Col s (Maybe a) -> Query s (Col s a)
+ Database.Selda.Nullable: nonNull :: (Same s t, SqlType a) => Col s (Maybe a) -> Query t (Col t a)
- Database.Selda.Nullable: restrict' :: Col s (Maybe Bool) -> Query s ()
+ Database.Selda.Nullable: restrict' :: Same s t => Col s (Maybe Bool) -> Query t ()

Files

ChangeLog.md view
@@ -1,6 +1,15 @@ # Revision history for Selda  +## 0.5.0.0 -- 2019-09-21++* index and indexUsing now accept a Group instead of a Selector (#121)+* Custom type errors for scope mismatches.+* Provide Generic instances for ID and RowID.+* Provide To/FromJSON instances for ID and RowID (selda-json).+* Add back MonadTrans instance for SeldaT.++ ## 0.4.0.0 -- 2019-06-02  * Type-safe support for backend-specific functionality. Top level query definitions now require explicit type signature. (#80)
LICENSE view
@@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017-2018 Anton Ekblad+Copyright (c) 2017-2019 Anton Ekblad  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal
selda.cabal view
@@ -1,5 +1,5 @@ name:                selda-version:             0.4.0.0+version:             0.5.0.0 synopsis:            Multi-backend, high-level EDSL for interacting with SQL databases. description:         This package provides an EDSL for writing portable, type-safe, high-level                      database code. Its feature set includes querying and modifying databases,
src/Database/Selda.hs view
@@ -21,7 +21,7 @@ --   basics. module Database.Selda   ( -- * Running queries-    MonadSelda+    MonadSelda, Backend   , SeldaError (..), ValidationError   , SeldaT, SeldaM   , Relational, Only (..), The (..)@@ -32,7 +32,7 @@      -- * Constructing queries   , SqlType (..), SqlRow (..), SqlEnum (..)-  , Columns+  , Columns, Same   , Order (..)   , (:*:)(..)   , select, selectValues, from, distinct@@ -278,8 +278,8 @@ --   semantics are used. This means that comparing to a @NULL@ field will remove --   the row in question from the current set. --   To test for @NULL@, use 'isNull' instead of @.== literal Nothing@.-(.==), (./=) :: SqlType a => Col s a -> Col s a -> Col s Bool-(.>), (.<), (.>=), (.<=) :: SqlOrd a => Col s a -> Col s a -> Col s Bool+(.==), (./=) :: (Same s t, SqlType a) => Col s a -> Col t a -> Col s Bool+(.>), (.<), (.>=), (.<=) :: (Same s t, SqlOrd a) => Col s a -> Col t a -> Col s Bool (.==) = liftC2 $ BinOp Eq (./=) = liftC2 $ BinOp Neq (.>)  = liftC2 $ BinOp Gt@@ -301,16 +301,16 @@ --   and returns the given default value where it is. -- --   This is the Selda equivalent of 'maybe'.-matchNull :: (SqlType a, SqlType b)+matchNull :: (SqlType a, SqlType b, Same s t)           => Col s b           -> (Col s a -> Col s b)-          -> Col s (Maybe a)+          -> Col t (Maybe a)           -> Col s b matchNull nullvalue f x = ifThenElse (isNull x) nullvalue (f (cast x))  -- | If the second value is Nothing, return the first value. Otherwise return --   the second value.-ifNull :: SqlType a => Col s a -> Col s (Maybe a) -> Col s a+ifNull :: (Same s t, SqlType a) => Col s a -> Col t (Maybe a) -> Col s a ifNull nullvalue x = ifThenElse (isNull x) nullvalue (cast x)  -- | Any container type which can be mapped over.@@ -334,7 +334,7 @@ -- | Any container type for which we can check object membership. class Set set where   -- | Is the given column contained in the given set?-  isIn :: SqlType a => Col s a -> set (Col s a) -> Col s Bool+  isIn :: (Same s t, SqlType a) => Col s a -> set (Col t a) -> Col s Bool infixl 4 `isIn`  instance Set [] where@@ -344,7 +344,7 @@ instance Set (Query s) where   isIn (One x) = One . InQuery x . snd . compQueryWithFreshScope -(.&&), (.||) :: Col s Bool -> Col s Bool -> Col s Bool+(.&&), (.||) :: Same s t => Col s Bool -> Col t Bool -> Col s Bool (.&&) = liftC2 $ BinOp And (.||) = liftC2 $ BinOp Or infixr 3 .&&@@ -374,8 +374,8 @@  -- | Returns 'true' if the given field in the given row is equal to the given --   literal.-is :: SqlType c => Selector r c -> c -> Row s r -> Col s Bool-is s x r = r ! s .== literal x+is :: forall r s c. SqlType c => Selector r c -> c -> Row s r -> Col s Bool+is s x r = r ! s .== (literal x :: Col s c)  -- | SQL NULL, at any type you like. null_ :: SqlType a => Col s (Maybe a)@@ -402,7 +402,7 @@ --   For instance: -- -- > "%gon" `like` "dragon" .== true-like :: Col s Text -> Col s Text -> Col s Bool+like :: Same s t => Col s Text -> Col t Text -> Col s Bool like = liftC2 $ BinOp Like infixl 4 `like` @@ -458,5 +458,5 @@ toString = cast  -- | Perform a conditional on a column-ifThenElse :: SqlType a => Col s Bool -> Col s a -> Col s a -> Col s a+ifThenElse :: (Same s t, Same t u, SqlType a) => Col s Bool -> Col t a -> Col u a -> Col s a ifThenElse = liftC3 If
src/Database/Selda/Backend/Internal.hs view
@@ -259,6 +259,9 @@   type Backend (SeldaT b m) = b   withConnection m = S get >>= m +instance MonadTrans (SeldaT b) where+  lift = S . lift+ -- | The simplest form of Selda computation; 'SeldaT' specialized to 'IO'. type SeldaM b = SeldaT b IO 
src/Database/Selda/Column.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE GADTs, TypeFamilies, TypeOperators, PolyKinds #-} {-# LANGUAGE FlexibleInstances, OverloadedStrings, ScopedTypeVariables #-}+{-# LANGUAGE DataKinds, UndecidableInstances, MultiParamTypeClasses #-} -- | Columns and associated utility functions, specialized to 'SQL'. module Database.Selda.Column-  ( Columns+  ( Columns, Same   , Row (..), Col (..), SomeCol (..), UntypedCol (..)   , Exp (..), NulOp (..), UnOp (..), BinOp (..)   , toTup, fromTup, liftC, liftC2, liftC3@@ -18,6 +19,7 @@ import Data.Proxy import Data.String import Data.Text (Text)+import GHC.TypeLits as TL  -- | Any column tuple. class Columns a where@@ -66,8 +68,15 @@        -> Col s d liftC3 f (One a) (One b) (One c) = One (f a b c) -liftC2 :: (Exp SQL a -> Exp SQL b -> Exp SQL c) -> Col s a -> Col s b -> Col s c-liftC2 f (One a) (One b) = One (f a b)+-- | Denotes that scopes @s@ and @t@ are identical.+class s ~ t => Same s t where+  liftC2 :: (Exp SQL a -> Exp SQL b -> Exp SQL c) -> Col s a -> Col t b -> Col s c+  liftC2 f (One a) (One b) = One (f a b)++instance {-# OVERLAPPING #-} Same s s+instance {-# OVERLAPPABLE #-} (s ~ t, TypeError+  ('TL.Text "An identifier from an outer scope may not be used in an inner query."))+  => Same s t  liftC :: (Exp SQL a -> Exp SQL b) -> Col s a -> Col s b liftC f (One x) = One (f x)
src/Database/Selda/Migrations.hs view
@@ -41,8 +41,8 @@ --   Both table schemas are validated before starting the migration, and the --   source table is validated against what's currently in the database. -----   The migration is performed as a migration, ensuring that either the entire---   migration passes, or none of it does.+--   The migration is performed as a transaction, ensuring that either the+--   entire migration passes, or none of it does. migrate :: (MonadSelda m, MonadMask m, Relational a, Relational b)         => Table a -- ^ Table to migrate from.         -> Table b -- ^ Table to migrate to.
src/Database/Selda/Nullable.hs view
@@ -28,16 +28,16 @@ fromNullable :: SqlType (NonNull a) => Col s a -> Col s (NonNull a) fromNullable = unsafeCoerce -(?==), (?/=) :: (a :?~ b, SqlType a) => Col s a -> Col s b -> Col s (Maybe Bool)+(?==), (?/=) :: (a :?~ b, SqlType a, Same s t) => Col s a -> Col t b -> Col s (Maybe Bool) -(?>), (?<), (?>=), (?<=) :: (a :?~ b, SqlOrd (NonNull a))-                         => Col s a -> Col s b -> Col s (Maybe Bool)+(?>), (?<), (?>=), (?<=) :: (a :?~ b, SqlOrd (NonNull a), Same s t)+                         => Col s a -> Col t b -> Col s (Maybe Bool) -(?+), (?-), (?*) :: (a :?~ b, Num (NonNull a))-                 => Col s a -> Col s b -> Col s (Maybe (NonNull a))+(?+), (?-), (?*) :: (a :?~ b, Num (NonNull a), Same s t)+                 => Col s a -> Col t b -> Col s (Maybe (NonNull a)) -(?/) :: (a :?~ b, Fractional (Col s (NonNull a)))-     => Col s a -> Col s b -> Col s (Maybe (NonNull a))+(?/) :: (a :?~ b, Fractional (Col s (NonNull a)), Same s t)+     => Col s a -> Col t b -> Col s (Maybe (NonNull a))  a ?== b = cast $ fromNullable a .== fromNullable b a ?/= b = cast $ fromNullable a ./= fromNullable b@@ -69,12 +69,12 @@  -- | Converts a nullable column into a non-nullable one, yielding the empty --   result set if the column is null.-nonNull :: SqlType a => Col s (Maybe a) -> Query s (Col s a)+nonNull :: (Same s t, SqlType a) => Col s (Maybe a) -> Query t (Col t a) nonNull x = do   restrict (not_ $ isNull x)   return (fromNullable x)  -- | Restrict a query using a nullable expression. --   Equivalent to @restrict . ifNull false@.-restrict' :: Col s (Maybe Bool) -> Query s ()+restrict' :: Same s t => Col s (Maybe Bool) -> Query t () restrict' = restrict . fromNullable
src/Database/Selda/Query.hs view
@@ -52,7 +52,7 @@     defToVal (Right x) = x  -- | Restrict the query somehow. Roughly equivalent to @WHERE@.-restrict :: Col s Bool -> Query s ()+restrict :: Same s t => Col s Bool -> Query t () restrict (One p) = Query $ do     st <- get     put $ case sources st of@@ -168,7 +168,7 @@ -- >   person <- select people -- >   name' <- groupBy (person ! name) -- >   return (name' :*: count(person ! pet_name) .> 0)-groupBy :: SqlType a => Col (Inner s) a -> Query (Inner s) (Aggr (Inner s) a)+groupBy :: (Same s t, SqlType a) => Col (Inner s) a -> Query (Inner t) (Aggr (Inner t) a) groupBy (One c) = Query $ do   st <- get   put $ st {groupCols = Some c : groupCols st}@@ -176,7 +176,7 @@  -- | Drop the first @m@ rows, then get at most @n@ of the remaining rows from the --   given subquery.-limit :: Int -> Int -> Query (Inner s) a -> Query s (OuterCols a)+limit :: Same s t => Int -> Int -> Query (Inner s) a -> Query t (OuterCols a) limit from to q = Query $ do   (lim_st, res) <- isolate q   st <- get@@ -208,7 +208,7 @@ --   is buried somewhere deep in an earlier query. --   However, the ordering must always be stable, to ensure that previous --   calls to order are not simply erased.-order :: SqlType a => Col s a -> Order -> Query s ()+order :: (Same s t, SqlType a) => Col s a -> Order -> Query t () order (One c) o = Query $ do   st <- get   case sources st of
src/Database/Selda/SqlType.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE GADTs, OverloadedStrings, ScopedTypeVariables, FlexibleInstances #-}-{-# LANGUAGE UndecidableInstances, DefaultSignatures #-}+{-# LANGUAGE UndecidableInstances, DefaultSignatures, DeriveGeneric #-} -- | Types representable as columns in Selda's subset of SQL. module Database.Selda.SqlType   ( SqlType (..), SqlEnum (..)@@ -18,6 +18,7 @@ import Data.Time import Data.Typeable import Data.UUID.Types (UUID, toString, fromByteString, nil)+import GHC.Generics (Generic)  -- | Format string used to represent date and time when --   representing timestamps as text.@@ -199,7 +200,7 @@ -- | A row identifier for some table. --   This is the type of auto-incrementing primary keys. newtype RowID = RowID Int-  deriving (Eq, Ord, Typeable)+  deriving (Eq, Ord, Typeable, Generic) instance Show RowID where   show (RowID n) = show n @@ -227,7 +228,7 @@ --   "Database.Selda.Unsafe" module if you for some reason need to add a type --   to a row identifier. newtype ID a = ID {untyped :: RowID}-  deriving (Eq, Ord, Typeable)+  deriving (Eq, Ord, Typeable, Generic) instance Show (ID a) where   show = show . untyped 
src/Database/Selda/Table.hs view
@@ -15,7 +15,7 @@   , untypedAutoPrimary, weakUntypedAutoPrimary   , unique   , index, indexUsing-  , tableExpr, indexedCols+  , tableExpr   , isAutoPrimary, isPrimary, isUnique   ) where import Data.Text (Text)@@ -122,9 +122,10 @@       | sel :- Attribute [a] <- attrs       , let ixs = indices sel       , case ixs of-          (_:_:_)           -> True-          [_] | a == Unique -> True-          _                 -> False+          (_:_:_)              -> True+          [_] | a == Unique    -> True+          [_] | Indexed _ <- a -> True+          _                    -> False       ]     pkAttrs = concat       [ [(ixs, Primary), (ixs, Required)]@@ -173,12 +174,12 @@ primary :: Attribute Group t a primary = Attribute [Primary, Required] --- | Create an index on this column.-index :: Attribute Selector t c+-- | Create an index on these column(s).+index :: Attribute Group t c index = Attribute [Indexed Nothing]  -- | Create an index using the given index method on this column.-indexUsing :: IndexMethod -> Attribute Selector t c+indexUsing :: IndexMethod -> Attribute Group t c indexUsing m = Attribute [Indexed (Just m)]  -- | An auto-incrementing primary key.
src/Database/Selda/Table/Compile.hs view
@@ -3,13 +3,15 @@ module Database.Selda.Table.Compile where import Database.Selda.Table import Database.Selda.Table.Validation+import           Data.IntMap (IntMap)+import qualified Data.IntMap as IntMap import Data.List (foldl') #if !MIN_VERSION_base(4, 11, 0) import Data.Monoid #endif import Data.Text (Text, intercalate, pack) import qualified Data.Text as Text-import Database.Selda.SQL hiding (param)+import Database.Selda.SQL hiding (param,cols) import Database.Selda.SQL.Print.Config import Database.Selda.SqlType (SqlTypeRep(..)) import Database.Selda.Types@@ -49,29 +51,50 @@ -- | Compile the @CREATE INDEX@ queries for all indexes on the given table. compileCreateIndexes :: PPConfig -> OnError -> Table a -> [Text] compileCreateIndexes cfg ifex tbl =-  [ compileCreateIndex cfg ifex (tableName tbl) col mmethod-  | (col, mmethod) <- indexedCols tbl+  [ compileCreateIndex cfg ifex (tableName tbl) (colNameOfIdx <$> idxs) mmethod+  | (idxs, Indexed mmethod) <- tableAttrs tbl   ]+ where+ idxMap :: IntMap ColName+ idxMap = IntMap.fromList (zip [0..] (colName <$> tableCols tbl))+ colNameOfIdx :: Int -> ColName+ colNameOfIdx colIdx =+    case IntMap.lookup colIdx idxMap of+        Nothing   -> error "Impossible: Index has non-existant column-index."+        Just name -> name --- | Get the name to use for an index on the given column in the given table.-indexNameFor :: TableName -> ColName -> Text-indexNameFor t c =-  fromColName $ addColPrefix c ("ix" <> rawTableName t <> "_")+-- | Get the name to use for an index on the given column(s) in the given table.+--+-- To ensure uniqueness+--+-- 1. Name multi-column indexes by connecting column names+--    with underscores.+-- 2. Escape underscores in column names.+--+-- Thus the index of columns @["foo","bar"]@ becomes @ixTable_foo_bar@ while+-- the index @["foo_bar"]@ receives an extra underscore to become+-- @ixTable_foo__bar@.+indexNameFor :: TableName -> [ColName] -> Text+indexNameFor t cs =+  let escUnderscore c = modColName c (Text.replace "_" "__") in+  let ixPrefix partial = "ix" <> rawTableName t <> "_" <> partial+  in ixPrefix (intercalateColNames "_" (escUnderscore <$> cs))  -- | Compile a @CREATE INDEX@ query for the given index. compileCreateIndex :: PPConfig                    -> OnError                    -> TableName-                   -> ColName+                   -> [ColName]                    -> Maybe IndexMethod                    -> Text-compileCreateIndex cfg ifex tbl col mmethod = mconcat-  [ "CREATE INDEX ", indexNameFor tbl col, " ON ", fromTableName tbl+compileCreateIndex cfg ifex tbl cols mmethod = mconcat+  [ "CREATE INDEX"+  , if ifex == Ignore then " IF NOT EXISTS " else " "+  , indexNameFor tbl cols, " ON ", fromTableName tbl   , case mmethod of-      Just method -> " " <> ppIndexMethodHook cfg method-      _           -> ""-  , " (", fromColName col, ")"-  , if ifex == Ignore then " IF NOT EXISTS" else ""+        Just method -> " " <> ppIndexMethodHook cfg method+        Nothing     -> ""+  , " (", Text.intercalate ", " (map fromColName cols), ")"   ]  -- | Compile a foreign key constraint.
src/Database/Selda/Table/Type.hs view
@@ -23,14 +23,6 @@   , tableAttrs :: [([Int], ColAttr)]   } --- | Get all table columns with an explicit index.-indexedCols :: Table a -> [(ColName, Maybe IndexMethod)]-indexedCols t =-  [ (colName col, mmethod)-  | col <- tableCols t-  , Indexed mmethod <- colAttrs col-  ]- -- | A complete description of a database column. data ColInfo = ColInfo   { colName  :: ColName
src/Database/Selda/Types.hs view
@@ -9,15 +9,15 @@   , first, second, third, fourth, fifth   , ColName, TableName   , modColName, mkColName, mkTableName, addColSuffix, addColPrefix-  , fromColName, fromTableName, rawTableName+  , fromColName, fromTableName, rawTableName, intercalateColNames   ) where import Data.Dynamic import Data.String-import Data.Text (Text, replace, append)+import Data.Text (Text, replace, append, intercalate) import GHC.Generics (Generic)  -- | Name of a database column.-newtype ColName = ColName Text+newtype ColName = ColName { unColName :: Text }   deriving (Ord, Eq, Show, IsString)  -- | Name of a database table.@@ -39,6 +39,15 @@ -- | Convert a column name into a string, with quotes. fromColName :: ColName -> Text fromColName (ColName cn) = mconcat ["\"", escapeQuotes cn, "\""]++-- | Convert column names into a string, without quotes, intercalating the given+-- string.+--+-- @+-- intercalateColNames "_" [ColName "a", ColName "b"] == "a_b"+-- @+intercalateColNames :: Text -> [ColName] -> Text+intercalateColNames inter cs = intercalate inter (escapeQuotes . unColName <$> cs)  -- | Convert a table name into a string, with quotes. fromTableName :: TableName -> Text