packages feed

pg-store 0.1.1 → 0.2

raw patch · 21 files changed

+1863/−1885 lines, 21 filesdep +aesondep +blaze-builderdep +haskell-src-metadep −QuickCheckdep −hspecdep −pg-storedep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: aeson, blaze-builder, haskell-src-meta, scientific

Dependencies removed: QuickCheck, hspec, pg-store

Dependency ranges changed: base

API changes (from Hackage documentation)

- Database.PostgreSQL.Store: BadResponse :: ExecStatus
- Database.PostgreSQL.Store: Check :: String -> TableConstraint
- Database.PostgreSQL.Store: CommandOk :: ExecStatus
- Database.PostgreSQL.Store: CopyIn :: ExecStatus
- Database.PostgreSQL.Store: CopyOut :: ExecStatus
- Database.PostgreSQL.Store: EmptyQuery :: ExecStatus
- Database.PostgreSQL.Store: EmptyResult :: ErrandError
- Database.PostgreSQL.Store: FatalError :: ExecStatus
- Database.PostgreSQL.Store: NonfatalError :: ExecStatus
- Database.PostgreSQL.Store: NullValue :: Value
- Database.PostgreSQL.Store: Reference :: Int64 -> Reference a
- Database.PostgreSQL.Store: ResultError :: ResultError -> ErrandError
- Database.PostgreSQL.Store: SelectorField :: String -> SelectorElement
- Database.PostgreSQL.Store: SelectorSpecial :: String -> SelectorElement
- Database.PostgreSQL.Store: Single :: a -> Single a
- Database.PostgreSQL.Store: SingleTuple :: ExecStatus
- Database.PostgreSQL.Store: TooFewColumnsError :: Column -> ResultError
- Database.PostgreSQL.Store: TuplesOk :: ExecStatus
- Database.PostgreSQL.Store: Unique :: [Name] -> TableConstraint
- Database.PostgreSQL.Store: UnpackError :: Row -> Column -> Oid -> Format -> ResultError
- Database.PostgreSQL.Store: Value :: Oid -> ByteString -> Value
- Database.PostgreSQL.Store: [fromSingle] :: Single a -> a
- Database.PostgreSQL.Store: [referenceID] :: Reference a -> Int64
- Database.PostgreSQL.Store: [valueData] :: Value -> ByteString
- Database.PostgreSQL.Store: [valueType] :: Value -> Oid
- Database.PostgreSQL.Store: class Column a where columnAllowNull _proxy = False columnCheck _proxy _identifier = Nothing columnDescription proxy identifier = identifier ++ " " ++ columnTypeName proxy ++ if columnAllowNull proxy then "" else " NOT NULL" ++ case columnCheck proxy identifier of { Just stmt -> " CHECK (" ++ stmt ++ ")" Nothing -> "" }
- Database.PostgreSQL.Store: class QueryTable a
- Database.PostgreSQL.Store: class Result a
- Database.PostgreSQL.Store: class Table a where insertMany = mapM insert
- Database.PostgreSQL.Store: columnAllowNull :: Column a => Proxy a -> Bool
- Database.PostgreSQL.Store: columnCheck :: Column a => Proxy a -> String -> Maybe String
- Database.PostgreSQL.Store: columnDescription :: Column a => Proxy a -> String -> String
- Database.PostgreSQL.Store: columnTypeName :: Column a => Proxy a -> String
- Database.PostgreSQL.Store: createTableQuery :: Table a => Proxy a -> Query
- Database.PostgreSQL.Store: data ResultError
- Database.PostgreSQL.Store: data ResultProcessor a
- Database.PostgreSQL.Store: data SelectorElement
- Database.PostgreSQL.Store: data TableConstraint
- Database.PostgreSQL.Store: data Value
- Database.PostgreSQL.Store: delete :: Table a => Reference a -> Errand ()
- Database.PostgreSQL.Store: find :: Table a => Reference a -> Errand a
- Database.PostgreSQL.Store: mkCreateQuery :: Name -> Q Exp
- Database.PostgreSQL.Store: mkTable :: Name -> [TableConstraint] -> Q [Dec]
- Database.PostgreSQL.Store: newtype Reference a
- Database.PostgreSQL.Store: newtype Single a
- Database.PostgreSQL.Store: pack :: Column a => a -> Value
- Database.PostgreSQL.Store: pgss :: QuasiQuoter
- Database.PostgreSQL.Store: queryResultProcessor :: Result a => ResultProcessor a
- Database.PostgreSQL.Store: query_ :: Query -> Errand ()
- Database.PostgreSQL.Store: skipColumn :: ResultProcessor ()
- Database.PostgreSQL.Store: tableIDName :: QueryTable a => Proxy a -> String
- Database.PostgreSQL.Store: tableName :: QueryTable a => Proxy a -> String
- Database.PostgreSQL.Store: tableSelectors :: QueryTable a => Proxy a -> [SelectorElement]
- Database.PostgreSQL.Store: unpack :: Column a => Value -> Maybe a
- Database.PostgreSQL.Store: unpackColumn :: (Column a) => ResultProcessor a
- Database.PostgreSQL.Store: update :: Table a => Reference a -> a -> Errand ()
- Database.PostgreSQL.Store.Columns: NullValue :: Value
- Database.PostgreSQL.Store.Columns: Value :: Oid -> ByteString -> Value
- Database.PostgreSQL.Store.Columns: [valueData] :: Value -> ByteString
- Database.PostgreSQL.Store.Columns: [valueType] :: Value -> Oid
- Database.PostgreSQL.Store.Columns: class Column a where columnAllowNull _proxy = False columnCheck _proxy _identifier = Nothing columnDescription proxy identifier = identifier ++ " " ++ columnTypeName proxy ++ if columnAllowNull proxy then "" else " NOT NULL" ++ case columnCheck proxy identifier of { Just stmt -> " CHECK (" ++ stmt ++ ")" Nothing -> "" }
- Database.PostgreSQL.Store.Columns: columnAllowNull :: Column a => Proxy a -> Bool
- Database.PostgreSQL.Store.Columns: columnCheck :: Column a => Proxy a -> String -> Maybe String
- Database.PostgreSQL.Store.Columns: columnDescription :: Column a => Proxy a -> String -> String
- Database.PostgreSQL.Store.Columns: columnTypeName :: Column a => Proxy a -> String
- Database.PostgreSQL.Store.Columns: data Value
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column Data.ByteString.Internal.ByteString
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column Data.ByteString.Lazy.Internal.ByteString
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column Data.Text.Internal.Lazy.Text
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column Data.Text.Internal.Text
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column Data.Time.Clock.UTC.UTCTime
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column Database.PostgreSQL.Store.Columns.Value
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Int.Int16
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Int.Int32
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Int.Int64
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Int.Int8
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Integer.Type.Integer
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Types.Bool
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Types.Int
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Types.Word
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Word.Word16
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Word.Word32
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Word.Word64
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column GHC.Word.Word8
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column [GHC.Types.Char]
- Database.PostgreSQL.Store.Columns: instance Database.PostgreSQL.Store.Columns.Column a => Database.PostgreSQL.Store.Columns.Column (GHC.Base.Maybe a)
- Database.PostgreSQL.Store.Columns: instance GHC.Classes.Eq Database.PostgreSQL.Store.Columns.Value
- Database.PostgreSQL.Store.Columns: instance GHC.Classes.Ord Database.PostgreSQL.Store.Columns.Value
- Database.PostgreSQL.Store.Columns: instance GHC.Show.Show Database.PostgreSQL.Store.Columns.Value
- Database.PostgreSQL.Store.Columns: pack :: Column a => a -> Value
- Database.PostgreSQL.Store.Columns: unpack :: Column a => Value -> Maybe a
- Database.PostgreSQL.Store.Errand: BadResponse :: ExecStatus
- Database.PostgreSQL.Store.Errand: CommandOk :: ExecStatus
- Database.PostgreSQL.Store.Errand: CopyIn :: ExecStatus
- Database.PostgreSQL.Store.Errand: CopyOut :: ExecStatus
- Database.PostgreSQL.Store.Errand: EmptyQuery :: ExecStatus
- Database.PostgreSQL.Store.Errand: EmptyResult :: ErrandError
- Database.PostgreSQL.Store.Errand: FatalError :: ExecStatus
- Database.PostgreSQL.Store.Errand: NonfatalError :: ExecStatus
- Database.PostgreSQL.Store.Errand: ResultError :: ResultError -> ErrandError
- Database.PostgreSQL.Store.Errand: Single :: a -> Single a
- Database.PostgreSQL.Store.Errand: SingleTuple :: ExecStatus
- Database.PostgreSQL.Store.Errand: TuplesOk :: ExecStatus
- Database.PostgreSQL.Store.Errand: [fromSingle] :: Single a -> a
- Database.PostgreSQL.Store.Errand: class Result a
- Database.PostgreSQL.Store.Errand: data ExecStatus :: *
- Database.PostgreSQL.Store.Errand: instance (Database.PostgreSQL.Store.Errand.Result a, Database.PostgreSQL.Store.Errand.Result b) => Database.PostgreSQL.Store.Errand.Result (a, b)
- Database.PostgreSQL.Store.Errand: instance (Database.PostgreSQL.Store.Errand.Result a, Database.PostgreSQL.Store.Errand.Result b, Database.PostgreSQL.Store.Errand.Result c) => Database.PostgreSQL.Store.Errand.Result (a, b, c)
- Database.PostgreSQL.Store.Errand: instance (Database.PostgreSQL.Store.Errand.Result a, Database.PostgreSQL.Store.Errand.Result b, Database.PostgreSQL.Store.Errand.Result c, Database.PostgreSQL.Store.Errand.Result d) => Database.PostgreSQL.Store.Errand.Result (a, b, c, d)
- Database.PostgreSQL.Store.Errand: instance (Database.PostgreSQL.Store.Errand.Result a, Database.PostgreSQL.Store.Errand.Result b, Database.PostgreSQL.Store.Errand.Result c, Database.PostgreSQL.Store.Errand.Result d, Database.PostgreSQL.Store.Errand.Result e) => Database.PostgreSQL.Store.Errand.Result (a, b, c, d, e)
- Database.PostgreSQL.Store.Errand: instance (Database.PostgreSQL.Store.Errand.Result a, Database.PostgreSQL.Store.Errand.Result b, Database.PostgreSQL.Store.Errand.Result c, Database.PostgreSQL.Store.Errand.Result d, Database.PostgreSQL.Store.Errand.Result e, Database.PostgreSQL.Store.Errand.Result f) => Database.PostgreSQL.Store.Errand.Result (a, b, c, d, e, f)
- Database.PostgreSQL.Store.Errand: instance (Database.PostgreSQL.Store.Errand.Result a, Database.PostgreSQL.Store.Errand.Result b, Database.PostgreSQL.Store.Errand.Result c, Database.PostgreSQL.Store.Errand.Result d, Database.PostgreSQL.Store.Errand.Result e, Database.PostgreSQL.Store.Errand.Result f, Database.PostgreSQL.Store.Errand.Result g) => Database.PostgreSQL.Store.Errand.Result (a, b, c, d, e, f, g)
- Database.PostgreSQL.Store.Errand: instance Database.PostgreSQL.Store.Columns.Column a => Database.PostgreSQL.Store.Errand.Result (Database.PostgreSQL.Store.Errand.Single a)
- Database.PostgreSQL.Store.Errand: instance GHC.Classes.Eq a => GHC.Classes.Eq (Database.PostgreSQL.Store.Errand.Single a)
- Database.PostgreSQL.Store.Errand: instance GHC.Classes.Ord a => GHC.Classes.Ord (Database.PostgreSQL.Store.Errand.Single a)
- Database.PostgreSQL.Store.Errand: instance GHC.Show.Show a => GHC.Show.Show (Database.PostgreSQL.Store.Errand.Single a)
- Database.PostgreSQL.Store.Errand: newtype Single a
- Database.PostgreSQL.Store.Errand: queryResultProcessor :: Result a => ResultProcessor a
- Database.PostgreSQL.Store.Errand: query_ :: Query -> Errand ()
- Database.PostgreSQL.Store.OIDs: bool :: OIDQ
- Database.PostgreSQL.Store.OIDs: bytea :: OIDQ
- Database.PostgreSQL.Store.OIDs: char :: OIDQ
- Database.PostgreSQL.Store.OIDs: float4 :: OIDQ
- Database.PostgreSQL.Store.OIDs: float8 :: OIDQ
- Database.PostgreSQL.Store.OIDs: instance Database.PostgreSQL.Store.OIDs.GenOID Language.Haskell.TH.Syntax.Exp
- Database.PostgreSQL.Store.OIDs: instance Database.PostgreSQL.Store.OIDs.GenOID Language.Haskell.TH.Syntax.Pat
- Database.PostgreSQL.Store.OIDs: int2 :: OIDQ
- Database.PostgreSQL.Store.OIDs: int4 :: OIDQ
- Database.PostgreSQL.Store.OIDs: int8 :: OIDQ
- Database.PostgreSQL.Store.OIDs: numeric :: OIDQ
- Database.PostgreSQL.Store.OIDs: text :: OIDQ
- Database.PostgreSQL.Store.OIDs: timestamp :: OIDQ
- Database.PostgreSQL.Store.OIDs: timestamptz :: OIDQ
- Database.PostgreSQL.Store.OIDs: type OIDQ = forall a. GenOID a => Q a
- Database.PostgreSQL.Store.OIDs: varchar :: OIDQ
- Database.PostgreSQL.Store.Query: Query :: !ByteString -> ![Value] -> Query
- Database.PostgreSQL.Store.Query: SelectorField :: String -> SelectorElement
- Database.PostgreSQL.Store.Query: SelectorSpecial :: String -> SelectorElement
- Database.PostgreSQL.Store.Query: [queryParams] :: Query -> ![Value]
- Database.PostgreSQL.Store.Query: [queryStatement] :: Query -> !ByteString
- Database.PostgreSQL.Store.Query: class QueryBuildable s p o | s p -> o
- Database.PostgreSQL.Store.Query: class QueryCode s
- Database.PostgreSQL.Store.Query: class QueryTable a
- Database.PostgreSQL.Store.Query: data Query
- Database.PostgreSQL.Store.Query: data SelectorElement
- Database.PostgreSQL.Store.Query: instance Database.PostgreSQL.Store.Query.QueryBuildable Data.ByteString.Internal.ByteString Database.PostgreSQL.Store.Columns.Value Database.PostgreSQL.Store.Query.Query
- Database.PostgreSQL.Store.Query: instance Database.PostgreSQL.Store.Query.QueryBuildable GHC.Base.String (Language.Haskell.TH.Syntax.Q Language.Haskell.TH.Syntax.Exp) (Language.Haskell.TH.Syntax.Q Language.Haskell.TH.Syntax.Exp)
- Database.PostgreSQL.Store.Query: instance Database.PostgreSQL.Store.Query.QueryBuildable [Language.Haskell.TH.Syntax.Q Language.Haskell.TH.Syntax.Exp] (Language.Haskell.TH.Syntax.Q Language.Haskell.TH.Syntax.Exp) (Language.Haskell.TH.Syntax.Q Language.Haskell.TH.Syntax.Exp)
- Database.PostgreSQL.Store.Query: instance Database.PostgreSQL.Store.Query.QueryCode Data.ByteString.Internal.ByteString
- Database.PostgreSQL.Store.Query: instance Database.PostgreSQL.Store.Query.QueryCode GHC.Base.String
- Database.PostgreSQL.Store.Query: instance Database.PostgreSQL.Store.Query.QueryCode [Language.Haskell.TH.Syntax.Q Language.Haskell.TH.Syntax.Exp]
- Database.PostgreSQL.Store.Query: instance GHC.Classes.Eq Database.PostgreSQL.Store.Query.Query
- Database.PostgreSQL.Store.Query: instance GHC.Classes.Eq Database.PostgreSQL.Store.Query.SelectorElement
- Database.PostgreSQL.Store.Query: instance GHC.Classes.Ord Database.PostgreSQL.Store.Query.Query
- Database.PostgreSQL.Store.Query: instance GHC.Classes.Ord Database.PostgreSQL.Store.Query.SelectorElement
- Database.PostgreSQL.Store.Query: instance GHC.Show.Show Database.PostgreSQL.Store.Query.Query
- Database.PostgreSQL.Store.Query: instance GHC.Show.Show Database.PostgreSQL.Store.Query.SelectorElement
- Database.PostgreSQL.Store.Query: intercalateBuilder :: QueryBuilder s p -> [QueryBuilder s p] -> QueryBuilder s p
- Database.PostgreSQL.Store.Query: pgsq :: QuasiQuoter
- Database.PostgreSQL.Store.Query: pgss :: QuasiQuoter
- Database.PostgreSQL.Store.Query: quoteIdentifier :: String -> String
- Database.PostgreSQL.Store.Query: runQueryBuilder :: (QueryBuildable s p o, Monoid s) => QueryBuilder s p -> o
- Database.PostgreSQL.Store.Query: tableIDName :: QueryTable a => Proxy a -> String
- Database.PostgreSQL.Store.Query: tableName :: QueryTable a => Proxy a -> String
- Database.PostgreSQL.Store.Query: tableSelectors :: QueryTable a => Proxy a -> [SelectorElement]
- Database.PostgreSQL.Store.Query: type QueryBuilder s p = State (BuilderState s p) ()
- Database.PostgreSQL.Store.Query: writeAbsIdentifier :: (QueryCode s) => String -> String -> QueryBuilder s p
- Database.PostgreSQL.Store.Query: writeCode :: (QueryCode s) => Code s -> QueryBuilder s p
- Database.PostgreSQL.Store.Query: writeColumn :: (Column p, QueryCode s) => p -> QueryBuilder s Value
- Database.PostgreSQL.Store.Query: writeIdentifier :: (QueryCode s) => String -> QueryBuilder s p
- Database.PostgreSQL.Store.Query: writeParam :: (QueryCode s) => p -> QueryBuilder s p
- Database.PostgreSQL.Store.Query: writeStringCode :: (QueryCode s) => String -> QueryBuilder s p
- Database.PostgreSQL.Store.Result: TooFewColumnsError :: Column -> ResultError
- Database.PostgreSQL.Store.Result: UnpackError :: Row -> Column -> Oid -> Format -> ResultError
- Database.PostgreSQL.Store.Result: data ResultError
- Database.PostgreSQL.Store.Result: data ResultProcessor a
- Database.PostgreSQL.Store.Result: instance Control.Monad.Error.Class.MonadError Database.PostgreSQL.Store.Result.ResultError Database.PostgreSQL.Store.Result.ResultProcessor
- Database.PostgreSQL.Store.Result: instance Control.Monad.IO.Class.MonadIO Database.PostgreSQL.Store.Result.ResultProcessor
- Database.PostgreSQL.Store.Result: instance GHC.Base.Applicative Database.PostgreSQL.Store.Result.ResultProcessor
- Database.PostgreSQL.Store.Result: instance GHC.Base.Functor Database.PostgreSQL.Store.Result.ResultProcessor
- Database.PostgreSQL.Store.Result: instance GHC.Base.Monad Database.PostgreSQL.Store.Result.ResultProcessor
- Database.PostgreSQL.Store.Result: instance GHC.Classes.Eq Database.PostgreSQL.Store.Result.ResultError
- Database.PostgreSQL.Store.Result: instance GHC.Show.Show Database.PostgreSQL.Store.Result.ResultError
- Database.PostgreSQL.Store.Result: processOneResult :: Result -> ResultProcessor a -> ExceptT ResultError IO (Maybe a)
- Database.PostgreSQL.Store.Result: processResult :: Result -> ResultProcessor a -> ExceptT ResultError IO [a]
- Database.PostgreSQL.Store.Result: skipColumn :: ResultProcessor ()
- Database.PostgreSQL.Store.Result: unpackColumn :: (Column a) => ResultProcessor a
- Database.PostgreSQL.Store.Table: Check :: String -> TableConstraint
- Database.PostgreSQL.Store.Table: Reference :: Int64 -> Reference a
- Database.PostgreSQL.Store.Table: Unique :: [Name] -> TableConstraint
- Database.PostgreSQL.Store.Table: [referenceID] :: Reference a -> Int64
- Database.PostgreSQL.Store.Table: class Table a where insertMany = mapM insert
- Database.PostgreSQL.Store.Table: createTableQuery :: Table a => Proxy a -> Query
- Database.PostgreSQL.Store.Table: data TableConstraint
- Database.PostgreSQL.Store.Table: delete :: Table a => Reference a -> Errand ()
- Database.PostgreSQL.Store.Table: find :: Table a => Reference a -> Errand a
- Database.PostgreSQL.Store.Table: insert :: Table a => a -> Errand (Reference a)
- Database.PostgreSQL.Store.Table: insertMany :: Table a => [a] -> Errand [Reference a]
- Database.PostgreSQL.Store.Table: instance Database.PostgreSQL.Store.Errand.Result (Database.PostgreSQL.Store.Table.Reference a)
- Database.PostgreSQL.Store.Table: instance Database.PostgreSQL.Store.Query.QueryTable a => Database.PostgreSQL.Store.Columns.Column (Database.PostgreSQL.Store.Table.Reference a)
- Database.PostgreSQL.Store.Table: instance GHC.Classes.Eq (Database.PostgreSQL.Store.Table.Reference a)
- Database.PostgreSQL.Store.Table: instance GHC.Classes.Eq Database.PostgreSQL.Store.Table.TableConstraint
- Database.PostgreSQL.Store.Table: instance GHC.Classes.Ord (Database.PostgreSQL.Store.Table.Reference a)
- Database.PostgreSQL.Store.Table: instance GHC.Classes.Ord Database.PostgreSQL.Store.Table.TableConstraint
- Database.PostgreSQL.Store.Table: instance GHC.Show.Show (Database.PostgreSQL.Store.Table.Reference a)
- Database.PostgreSQL.Store.Table: instance GHC.Show.Show Database.PostgreSQL.Store.Table.TableConstraint
- Database.PostgreSQL.Store.Table: mkCreateQuery :: Name -> Q Exp
- Database.PostgreSQL.Store.Table: mkTable :: Name -> [TableConstraint] -> Q [Dec]
- Database.PostgreSQL.Store.Table: newtype Reference a
- Database.PostgreSQL.Store.Table: update :: Table a => Reference a -> a -> Errand ()
+ Database.PostgreSQL.Store: Column :: ByteString -> ColumnType -> Column
+ Database.PostgreSQL.Store: ColumnRejected :: TypedValue -> RowErrorDetail
+ Database.PostgreSQL.Store: ColumnType :: ByteString -> Bool -> Maybe (ByteString -> QueryBuilder) -> ColumnType
+ Database.PostgreSQL.Store: ContentsRejected :: (Maybe ByteString) -> RowErrorDetail
+ Database.PostgreSQL.Store: ParseError :: RowError -> ErrandError
+ Database.PostgreSQL.Store: RowError :: RowErrorLocation -> RowErrorDetail -> RowError
+ Database.PostgreSQL.Store: RowErrorLocation :: Column -> Row -> RowErrorLocation
+ Database.PostgreSQL.Store: Table :: ByteString -> [Column] -> Table
+ Database.PostgreSQL.Store: TooFewColumns :: RowErrorDetail
+ Database.PostgreSQL.Store: [colName] :: Column -> ByteString
+ Database.PostgreSQL.Store: [colTypeCheck] :: ColumnType -> Maybe (ByteString -> QueryBuilder)
+ Database.PostgreSQL.Store: [colTypeName] :: ColumnType -> ByteString
+ Database.PostgreSQL.Store: [colTypeNotNull] :: ColumnType -> Bool
+ Database.PostgreSQL.Store: [colType] :: Column -> ColumnType
+ Database.PostgreSQL.Store: [tableCols] :: Table -> [Column]
+ Database.PostgreSQL.Store: [tableName] :: Table -> ByteString
+ Database.PostgreSQL.Store: castQuery :: Query a -> Query b
+ Database.PostgreSQL.Store: class (Entity a) => ColumnEntity a
+ Database.PostgreSQL.Store: class Entity a where insertEntity = insertGeneric parseEntity = parseGeneric
+ Database.PostgreSQL.Store: class (Entity a) => TableEntity a where describeTableType = describeGenericTable
+ Database.PostgreSQL.Store: create :: (TableEntity a) => proxy a -> Errand ()
+ Database.PostgreSQL.Store: data Column
+ Database.PostgreSQL.Store: data ColumnType
+ Database.PostgreSQL.Store: data RowError
+ Database.PostgreSQL.Store: data RowErrorDetail
+ Database.PostgreSQL.Store: data RowErrorLocation
+ Database.PostgreSQL.Store: data Table
+ Database.PostgreSQL.Store: deleteAll :: (TableEntity a) => proxy a -> Errand Int
+ Database.PostgreSQL.Store: describeColumnType :: ColumnEntity a => proxy a -> ColumnType
+ Database.PostgreSQL.Store: describeTableType :: (TableEntity a, GenericTable a) => proxy a -> Table
+ Database.PostgreSQL.Store: execute' :: Query a -> Errand Int
+ Database.PostgreSQL.Store: findAll :: (TableEntity a) => Errand [a]
+ Database.PostgreSQL.Store: insertEntity :: (Entity a, GenericEntity a, GEntity (AnalyzeEntity a)) => a -> QueryBuilder
+ Database.PostgreSQL.Store: parseEntity :: (Entity a, GenericEntity a, GEntity (AnalyzeEntity a)) => RowParser a
+ Database.PostgreSQL.Store.ColumnEntity: ColumnType :: ByteString -> Bool -> Maybe (ByteString -> QueryBuilder) -> ColumnType
+ Database.PostgreSQL.Store.ColumnEntity: [colTypeCheck] :: ColumnType -> Maybe (ByteString -> QueryBuilder)
+ Database.PostgreSQL.Store.ColumnEntity: [colTypeName] :: ColumnType -> ByteString
+ Database.PostgreSQL.Store.ColumnEntity: [colTypeNotNull] :: ColumnType -> Bool
+ Database.PostgreSQL.Store.ColumnEntity: class (Entity a) => ColumnEntity a
+ Database.PostgreSQL.Store.ColumnEntity: data ColumnType
+ Database.PostgreSQL.Store.ColumnEntity: describeColumnType :: ColumnEntity a => proxy a -> ColumnType
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity Data.Aeson.Types.Internal.Value
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity Data.ByteString.Internal.ByteString
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity Data.ByteString.Lazy.Internal.ByteString
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity Data.Scientific.Scientific
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity Data.Text.Internal.Lazy.Text
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity Data.Text.Internal.Text
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity Database.PostgreSQL.Store.ColumnEntity.PGInt2
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity Database.PostgreSQL.Store.ColumnEntity.PGInt4
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity Database.PostgreSQL.Store.ColumnEntity.PGInt8
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Base.String
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Int.Int16
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Int.Int32
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Int.Int64
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Int.Int8
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Integer.Type.Integer
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Natural.Natural
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Types.Bool
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Types.Double
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Types.Float
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Types.Int
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Types.Word
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Word.Word16
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Word.Word32
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Word.Word64
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity GHC.Word.Word8
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.ColumnEntity.ColumnEntity a => Database.PostgreSQL.Store.ColumnEntity.ColumnEntity (GHC.Base.Maybe a)
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.Entity.Entity Database.PostgreSQL.Store.ColumnEntity.PGInt2
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.Entity.Entity Database.PostgreSQL.Store.ColumnEntity.PGInt4
+ Database.PostgreSQL.Store.ColumnEntity: instance Database.PostgreSQL.Store.Entity.Entity Database.PostgreSQL.Store.ColumnEntity.PGInt8
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Classes.Eq Database.PostgreSQL.Store.ColumnEntity.PGInt2
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Classes.Eq Database.PostgreSQL.Store.ColumnEntity.PGInt4
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Classes.Eq Database.PostgreSQL.Store.ColumnEntity.PGInt8
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Classes.Ord Database.PostgreSQL.Store.ColumnEntity.PGInt2
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Classes.Ord Database.PostgreSQL.Store.ColumnEntity.PGInt4
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Classes.Ord Database.PostgreSQL.Store.ColumnEntity.PGInt8
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Enum.Bounded Database.PostgreSQL.Store.ColumnEntity.PGInt2
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Enum.Bounded Database.PostgreSQL.Store.ColumnEntity.PGInt4
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Enum.Bounded Database.PostgreSQL.Store.ColumnEntity.PGInt8
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Enum.Enum Database.PostgreSQL.Store.ColumnEntity.PGInt2
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Enum.Enum Database.PostgreSQL.Store.ColumnEntity.PGInt4
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Enum.Enum Database.PostgreSQL.Store.ColumnEntity.PGInt8
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Num.Num Database.PostgreSQL.Store.ColumnEntity.PGInt2
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Num.Num Database.PostgreSQL.Store.ColumnEntity.PGInt4
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Num.Num Database.PostgreSQL.Store.ColumnEntity.PGInt8
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Read.Read Database.PostgreSQL.Store.ColumnEntity.PGInt2
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Read.Read Database.PostgreSQL.Store.ColumnEntity.PGInt4
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Read.Read Database.PostgreSQL.Store.ColumnEntity.PGInt8
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Real.Integral Database.PostgreSQL.Store.ColumnEntity.PGInt2
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Real.Integral Database.PostgreSQL.Store.ColumnEntity.PGInt4
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Real.Integral Database.PostgreSQL.Store.ColumnEntity.PGInt8
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Real.Real Database.PostgreSQL.Store.ColumnEntity.PGInt2
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Real.Real Database.PostgreSQL.Store.ColumnEntity.PGInt4
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Real.Real Database.PostgreSQL.Store.ColumnEntity.PGInt8
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Show.Show Database.PostgreSQL.Store.ColumnEntity.PGInt2
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Show.Show Database.PostgreSQL.Store.ColumnEntity.PGInt4
+ Database.PostgreSQL.Store.ColumnEntity: instance GHC.Show.Show Database.PostgreSQL.Store.ColumnEntity.PGInt8
+ Database.PostgreSQL.Store.Entity: class Entity a where insertEntity = insertGeneric parseEntity = parseGeneric
+ Database.PostgreSQL.Store.Entity: class GEntity (dat :: KDataType)
+ Database.PostgreSQL.Store.Entity: class GEntityEnum (enum :: KFlatSum)
+ Database.PostgreSQL.Store.Entity: class GEntityRecord (rec :: KRecord)
+ Database.PostgreSQL.Store.Entity: gEnumValues :: GEntityEnum enum => [(ByteString, FlatSum enum)]
+ Database.PostgreSQL.Store.Entity: gInsertEntity :: GEntity dat => DataType dat -> QueryBuilder
+ Database.PostgreSQL.Store.Entity: gInsertEnum :: GEntityEnum enum => FlatSum enum -> QueryBuilder
+ Database.PostgreSQL.Store.Entity: gInsertRecord :: GEntityRecord rec => Record rec -> QueryBuilder
+ Database.PostgreSQL.Store.Entity: gParseEntity :: GEntity dat => RowParser (DataType dat)
+ Database.PostgreSQL.Store.Entity: gParseRecord :: GEntityRecord rec => RowParser (Record rec)
+ Database.PostgreSQL.Store.Entity: insertEntity :: (Entity a, GenericEntity a, GEntity (AnalyzeEntity a)) => a -> QueryBuilder
+ Database.PostgreSQL.Store.Entity: insertGeneric :: (GenericEntity a, GEntity (AnalyzeEntity a)) => a -> QueryBuilder
+ Database.PostgreSQL.Store.Entity: instance (Database.PostgreSQL.Store.Entity.Entity a, Database.PostgreSQL.Store.Entity.Entity b) => Database.PostgreSQL.Store.Entity.Entity (a, b)
+ Database.PostgreSQL.Store.Entity: instance (Database.PostgreSQL.Store.Entity.Entity a, Database.PostgreSQL.Store.Entity.Entity b, Database.PostgreSQL.Store.Entity.Entity c) => Database.PostgreSQL.Store.Entity.Entity (a, b, c)
+ Database.PostgreSQL.Store.Entity: instance (Database.PostgreSQL.Store.Entity.Entity a, Database.PostgreSQL.Store.Entity.Entity b, Database.PostgreSQL.Store.Entity.Entity c, Database.PostgreSQL.Store.Entity.Entity d) => Database.PostgreSQL.Store.Entity.Entity (a, b, c, d)
+ Database.PostgreSQL.Store.Entity: instance (Database.PostgreSQL.Store.Entity.Entity a, Database.PostgreSQL.Store.Entity.Entity b, Database.PostgreSQL.Store.Entity.Entity c, Database.PostgreSQL.Store.Entity.Entity d, Database.PostgreSQL.Store.Entity.Entity e) => Database.PostgreSQL.Store.Entity.Entity (a, b, c, d, e)
+ Database.PostgreSQL.Store.Entity: instance (Database.PostgreSQL.Store.Entity.Entity a, Database.PostgreSQL.Store.Entity.Entity b, Database.PostgreSQL.Store.Entity.Entity c, Database.PostgreSQL.Store.Entity.Entity d, Database.PostgreSQL.Store.Entity.Entity e, Database.PostgreSQL.Store.Entity.Entity f) => Database.PostgreSQL.Store.Entity.Entity (a, b, c, d, e, f)
+ Database.PostgreSQL.Store.Entity: instance (Database.PostgreSQL.Store.Entity.Entity a, Database.PostgreSQL.Store.Entity.Entity b, Database.PostgreSQL.Store.Entity.Entity c, Database.PostgreSQL.Store.Entity.Entity d, Database.PostgreSQL.Store.Entity.Entity e, Database.PostgreSQL.Store.Entity.Entity f, Database.PostgreSQL.Store.Entity.Entity g) => Database.PostgreSQL.Store.Entity.Entity (a, b, c, d, e, f, g)
+ Database.PostgreSQL.Store.Entity: instance (Database.PostgreSQL.Store.Entity.GEntityEnum lhs, Database.PostgreSQL.Store.Entity.GEntityEnum rhs) => Database.PostgreSQL.Store.Entity.GEntityEnum ('Database.PostgreSQL.Store.Generics.TChoose lhs rhs)
+ Database.PostgreSQL.Store.Entity: instance (Database.PostgreSQL.Store.Entity.GEntityRecord lhs, Database.PostgreSQL.Store.Entity.GEntityRecord rhs) => Database.PostgreSQL.Store.Entity.GEntityRecord ('Database.PostgreSQL.Store.Generics.TCombine lhs rhs)
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity Data.Aeson.Types.Internal.Value
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity Data.ByteString.Internal.ByteString
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity Data.ByteString.Lazy.Internal.ByteString
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity Data.Scientific.Scientific
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity Data.Text.Internal.Lazy.Text
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity Data.Text.Internal.Text
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity Database.PostgreSQL.Store.Query.Builder.QueryBuilder
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity Database.PostgreSQL.Store.Types.TypedValue
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity Database.PostgreSQL.Store.Types.Value
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Base.String
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Int.Int16
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Int.Int32
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Int.Int64
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Int.Int8
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Integer.Type.Integer
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Natural.Natural
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Types.Bool
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Types.Double
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Types.Float
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Types.Int
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Types.Word
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Word.Word16
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Word.Word32
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Word.Word64
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity GHC.Word.Word8
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity a => Database.PostgreSQL.Store.Entity.Entity (GHC.Base.Maybe a)
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.Entity typ => Database.PostgreSQL.Store.Entity.GEntityRecord ('Database.PostgreSQL.Store.Generics.TSingle meta typ)
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.GEntityEnum enum => Database.PostgreSQL.Store.Entity.GEntity ('Database.PostgreSQL.Store.Generics.TFlatSum d enum)
+ Database.PostgreSQL.Store.Entity: instance Database.PostgreSQL.Store.Entity.GEntityRecord rec => Database.PostgreSQL.Store.Entity.GEntity ('Database.PostgreSQL.Store.Generics.TRecord d c rec)
+ Database.PostgreSQL.Store.Entity: instance GHC.TypeLits.KnownSymbol name => Database.PostgreSQL.Store.Entity.GEntityEnum ('Database.PostgreSQL.Store.Generics.TValue ('GHC.Generics.MetaCons name f r))
+ Database.PostgreSQL.Store.Entity: parseEntity :: (Entity a, GenericEntity a, GEntity (AnalyzeEntity a)) => RowParser a
+ Database.PostgreSQL.Store.Entity: parseGeneric :: (GenericEntity a, GEntity (AnalyzeEntity a)) => RowParser a
+ Database.PostgreSQL.Store.Errand: ParseError :: RowError -> ErrandError
+ Database.PostgreSQL.Store.Errand: create :: (TableEntity a) => proxy a -> Errand ()
+ Database.PostgreSQL.Store.Errand: deleteAll :: (TableEntity a) => proxy a -> Errand Int
+ Database.PostgreSQL.Store.Errand: execute' :: Query a -> Errand Int
+ Database.PostgreSQL.Store.Errand: findAll :: (TableEntity a) => Errand [a]
+ Database.PostgreSQL.Store.Errand: insert :: (TableEntity a) => a -> Errand Bool
+ Database.PostgreSQL.Store.Errand: insertMany :: (TableEntity a) => [a] -> Errand Int
+ Database.PostgreSQL.Store.Generics: TChoose :: KFlatSum -> KFlatSum -> KFlatSum
+ Database.PostgreSQL.Store.Generics: TCombine :: KRecord -> KRecord -> KRecord
+ Database.PostgreSQL.Store.Generics: TFlatSum :: Meta -> KFlatSum -> KDataType
+ Database.PostgreSQL.Store.Generics: TRecord :: Meta -> Meta -> KRecord -> KDataType
+ Database.PostgreSQL.Store.Generics: TSingle :: Meta -> Type -> KRecord
+ Database.PostgreSQL.Store.Generics: TValue :: Meta -> KFlatSum
+ Database.PostgreSQL.Store.Generics: class GDataType (dat :: KDataType) where type DataTypeRep dat :: * -> * data DataType dat where {
+ Database.PostgreSQL.Store.Generics: class GFlatSum (enum :: KFlatSum) where type FlatSumRep enum :: * -> * data FlatSum enum where {
+ Database.PostgreSQL.Store.Generics: class GRecord (rec :: KRecord) where type RecordRep rec :: * -> * data Record rec where {
+ Database.PostgreSQL.Store.Generics: data KDataType
+ Database.PostgreSQL.Store.Generics: data KFlatSum
+ Database.PostgreSQL.Store.Generics: data KRecord
+ Database.PostgreSQL.Store.Generics: data family DataType dat;
+ Database.PostgreSQL.Store.Generics: fromDataType :: GDataType dat => DataType dat -> DataTypeRep dat x
+ Database.PostgreSQL.Store.Generics: fromFlatSum :: GFlatSum enum => FlatSum enum -> FlatSumRep enum x
+ Database.PostgreSQL.Store.Generics: fromGenericEntity :: (GenericEntity a) => a -> EntityDataType a
+ Database.PostgreSQL.Store.Generics: fromRecord :: GRecord rec => Record rec -> RecordRep rec x
+ Database.PostgreSQL.Store.Generics: instance (Database.PostgreSQL.Store.Generics.GFlatSum lhs, Database.PostgreSQL.Store.Generics.GFlatSum rhs) => Database.PostgreSQL.Store.Generics.GFlatSum ('Database.PostgreSQL.Store.Generics.TChoose lhs rhs)
+ Database.PostgreSQL.Store.Generics: instance (Database.PostgreSQL.Store.Generics.GRecord lhs, Database.PostgreSQL.Store.Generics.GRecord rhs) => Database.PostgreSQL.Store.Generics.GRecord ('Database.PostgreSQL.Store.Generics.TCombine lhs rhs)
+ Database.PostgreSQL.Store.Generics: instance (GHC.Show.Show (Database.PostgreSQL.Store.Generics.FlatSum lhs), GHC.Show.Show (Database.PostgreSQL.Store.Generics.FlatSum rhs)) => GHC.Show.Show (Database.PostgreSQL.Store.Generics.FlatSum ('Database.PostgreSQL.Store.Generics.TChoose lhs rhs))
+ Database.PostgreSQL.Store.Generics: instance (GHC.Show.Show (Database.PostgreSQL.Store.Generics.Record lhs), GHC.Show.Show (Database.PostgreSQL.Store.Generics.Record rhs)) => GHC.Show.Show (Database.PostgreSQL.Store.Generics.Record ('Database.PostgreSQL.Store.Generics.TCombine lhs rhs))
+ Database.PostgreSQL.Store.Generics: instance Database.PostgreSQL.Store.Generics.GFlatSum ('Database.PostgreSQL.Store.Generics.TValue meta)
+ Database.PostgreSQL.Store.Generics: instance Database.PostgreSQL.Store.Generics.GFlatSum enum => Database.PostgreSQL.Store.Generics.GDataType ('Database.PostgreSQL.Store.Generics.TFlatSum d enum)
+ Database.PostgreSQL.Store.Generics: instance Database.PostgreSQL.Store.Generics.GRecord ('Database.PostgreSQL.Store.Generics.TSingle meta typ)
+ Database.PostgreSQL.Store.Generics: instance Database.PostgreSQL.Store.Generics.GRecord rec => Database.PostgreSQL.Store.Generics.GDataType ('Database.PostgreSQL.Store.Generics.TRecord d c rec)
+ Database.PostgreSQL.Store.Generics: instance GHC.Show.Show (Database.PostgreSQL.Store.Generics.FlatSum ('Database.PostgreSQL.Store.Generics.TValue meta))
+ Database.PostgreSQL.Store.Generics: instance GHC.Show.Show (Database.PostgreSQL.Store.Generics.FlatSum enum) => GHC.Show.Show (Database.PostgreSQL.Store.Generics.DataType ('Database.PostgreSQL.Store.Generics.TFlatSum d enum))
+ Database.PostgreSQL.Store.Generics: instance GHC.Show.Show (Database.PostgreSQL.Store.Generics.Record rec) => GHC.Show.Show (Database.PostgreSQL.Store.Generics.DataType ('Database.PostgreSQL.Store.Generics.TRecord d c rec))
+ Database.PostgreSQL.Store.Generics: instance GHC.Show.Show typ => GHC.Show.Show (Database.PostgreSQL.Store.Generics.Record ('Database.PostgreSQL.Store.Generics.TSingle meta typ))
+ Database.PostgreSQL.Store.Generics: toDataType :: GDataType dat => DataTypeRep dat x -> DataType dat
+ Database.PostgreSQL.Store.Generics: toFlatSum :: GFlatSum enum => FlatSumRep enum x -> FlatSum enum
+ Database.PostgreSQL.Store.Generics: toGenericEntity :: (GenericEntity a) => EntityDataType a -> a
+ Database.PostgreSQL.Store.Generics: toRecord :: GRecord rec => RecordRep rec x -> Record rec
+ Database.PostgreSQL.Store.Generics: type AnalyzeEntity a = AnalyzeDataType a (Rep a)
+ Database.PostgreSQL.Store.Generics: type EntityDataType a = DataType (AnalyzeEntity a)
+ Database.PostgreSQL.Store.Generics: type GenericEntity a = (Generic a, GDataType (AnalyzeEntity a), DataTypeRep (AnalyzeEntity a) ~ Rep a)
+ Database.PostgreSQL.Store.Generics: type family DataTypeRep dat :: * -> *;
+ Database.PostgreSQL.Store.Generics: }
+ Database.PostgreSQL.Store.Query: castQuery :: Query a -> Query b
+ Database.PostgreSQL.Store.Query.Builder: buildQuery :: FromQueryBuilder a => QueryBuilder -> a
+ Database.PostgreSQL.Store.Query.Builder: class FromQueryBuilder a
+ Database.PostgreSQL.Store.Query.Builder: insertCode :: ByteString -> QueryBuilder
+ Database.PostgreSQL.Store.Query.Builder: insertCommaSeperated :: [QueryBuilder] -> QueryBuilder
+ Database.PostgreSQL.Store.Query.Builder: insertName :: ByteString -> QueryBuilder
+ Database.PostgreSQL.Store.Query.Builder: insertQuote :: ByteString -> QueryBuilder
+ Database.PostgreSQL.Store.Query.Builder: insertTypedValue :: TypedValue -> QueryBuilder
+ Database.PostgreSQL.Store.Query.Builder: insertValue :: Value -> QueryBuilder
+ Database.PostgreSQL.Store.Query.Builder: insertValue' :: ByteString -> Value -> QueryBuilder
+ Database.PostgreSQL.Store.Query.Builder: instance Database.PostgreSQL.Store.Query.Builder.FromQueryBuilder (Data.ByteString.Internal.ByteString, [Database.PostgreSQL.Store.Types.TypedValue])
+ Database.PostgreSQL.Store.Query.Builder: instance Database.PostgreSQL.Store.Query.Builder.FromQueryBuilder (Database.PostgreSQL.Store.Types.Query a)
+ Database.PostgreSQL.Store.Query.Builder: instance Database.PostgreSQL.Store.Query.Builder.FromQueryBuilder Data.ByteString.Internal.ByteString
+ Database.PostgreSQL.Store.Query.Builder: instance Database.PostgreSQL.Store.Query.Builder.FromQueryBuilder Database.PostgreSQL.Store.Query.Builder.QueryBuilder
+ Database.PostgreSQL.Store.Query.Builder: type QueryBuilder = State BuilderState ()
+ Database.PostgreSQL.Store.Query.TH: instance GHC.Classes.Eq Database.PostgreSQL.Store.Query.TH.QuerySegment
+ Database.PostgreSQL.Store.Query.TH: instance GHC.Classes.Ord Database.PostgreSQL.Store.Query.TH.QuerySegment
+ Database.PostgreSQL.Store.Query.TH: instance GHC.Show.Show Database.PostgreSQL.Store.Query.TH.QuerySegment
+ Database.PostgreSQL.Store.Query.TH: parseQuery :: String -> Q Exp
+ Database.PostgreSQL.Store.Query.TH: pgsq :: QuasiQuoter
+ Database.PostgreSQL.Store.RowParser: ColumnRejected :: TypedValue -> RowErrorDetail
+ Database.PostgreSQL.Store.RowParser: ContentsRejected :: (Maybe ByteString) -> RowErrorDetail
+ Database.PostgreSQL.Store.RowParser: RowError :: RowErrorLocation -> RowErrorDetail -> RowError
+ Database.PostgreSQL.Store.RowParser: RowErrorLocation :: Column -> Row -> RowErrorLocation
+ Database.PostgreSQL.Store.RowParser: TooFewColumns :: RowErrorDetail
+ Database.PostgreSQL.Store.RowParser: columnNumber :: RowParser Column
+ Database.PostgreSQL.Store.RowParser: columnsLeft :: RowParser Column
+ Database.PostgreSQL.Store.RowParser: data RowError
+ Database.PostgreSQL.Store.RowParser: data RowErrorDetail
+ Database.PostgreSQL.Store.RowParser: data RowErrorLocation
+ Database.PostgreSQL.Store.RowParser: data RowParser a
+ Database.PostgreSQL.Store.RowParser: fetchColumn :: RowParser TypedValue
+ Database.PostgreSQL.Store.RowParser: fetchContents :: RowParser (Maybe ByteString)
+ Database.PostgreSQL.Store.RowParser: instance Control.Monad.Error.Class.MonadError Database.PostgreSQL.Store.RowParser.RowError Database.PostgreSQL.Store.RowParser.RowParser
+ Database.PostgreSQL.Store.RowParser: instance GHC.Base.Applicative Database.PostgreSQL.Store.RowParser.RowParser
+ Database.PostgreSQL.Store.RowParser: instance GHC.Base.Functor Database.PostgreSQL.Store.RowParser.RowParser
+ Database.PostgreSQL.Store.RowParser: instance GHC.Base.Monad Database.PostgreSQL.Store.RowParser.RowParser
+ Database.PostgreSQL.Store.RowParser: instance GHC.Classes.Eq Database.PostgreSQL.Store.RowParser.RowError
+ Database.PostgreSQL.Store.RowParser: instance GHC.Classes.Eq Database.PostgreSQL.Store.RowParser.RowErrorDetail
+ Database.PostgreSQL.Store.RowParser: instance GHC.Classes.Eq Database.PostgreSQL.Store.RowParser.RowErrorLocation
+ Database.PostgreSQL.Store.RowParser: instance GHC.Classes.Ord Database.PostgreSQL.Store.RowParser.RowError
+ Database.PostgreSQL.Store.RowParser: instance GHC.Classes.Ord Database.PostgreSQL.Store.RowParser.RowErrorDetail
+ Database.PostgreSQL.Store.RowParser: instance GHC.Classes.Ord Database.PostgreSQL.Store.RowParser.RowErrorLocation
+ Database.PostgreSQL.Store.RowParser: instance GHC.Show.Show Database.PostgreSQL.Store.RowParser.RowError
+ Database.PostgreSQL.Store.RowParser: instance GHC.Show.Show Database.PostgreSQL.Store.RowParser.RowErrorDetail
+ Database.PostgreSQL.Store.RowParser: instance GHC.Show.Show Database.PostgreSQL.Store.RowParser.RowErrorLocation
+ Database.PostgreSQL.Store.RowParser: parseColumn :: (TypedValue -> Maybe a) -> RowParser a
+ Database.PostgreSQL.Store.RowParser: parseContents :: (ByteString -> Maybe a) -> RowParser a
+ Database.PostgreSQL.Store.RowParser: parseResult :: Result -> RowParser a -> ExceptT RowError IO [a]
+ Database.PostgreSQL.Store.RowParser: peekColumn :: RowParser TypedValue
+ Database.PostgreSQL.Store.RowParser: peekContents :: RowParser (Maybe ByteString)
+ Database.PostgreSQL.Store.RowParser: rowNumber :: RowParser Row
+ Database.PostgreSQL.Store.RowParser: skipColumn :: RowParser ()
+ Database.PostgreSQL.Store.Table: Column :: ByteString -> ColumnType -> Column
+ Database.PostgreSQL.Store.Table: ColumnType :: ByteString -> Bool -> Maybe (ByteString -> QueryBuilder) -> ColumnType
+ Database.PostgreSQL.Store.Table: TCombine :: KColumns -> KColumns -> KColumns
+ Database.PostgreSQL.Store.Table: TSelector :: Symbol -> Type -> KColumns
+ Database.PostgreSQL.Store.Table: TTable :: Symbol -> KColumns -> KTable
+ Database.PostgreSQL.Store.Table: Table :: ByteString -> [Column] -> Table
+ Database.PostgreSQL.Store.Table: [colName] :: Column -> ByteString
+ Database.PostgreSQL.Store.Table: [colTypeCheck] :: ColumnType -> Maybe (ByteString -> QueryBuilder)
+ Database.PostgreSQL.Store.Table: [colTypeName] :: ColumnType -> ByteString
+ Database.PostgreSQL.Store.Table: [colTypeNotNull] :: ColumnType -> Bool
+ Database.PostgreSQL.Store.Table: [colType] :: Column -> ColumnType
+ Database.PostgreSQL.Store.Table: [tableCols] :: Table -> [Column]
+ Database.PostgreSQL.Store.Table: [tableName] :: Table -> ByteString
+ Database.PostgreSQL.Store.Table: buildTableSchema :: Table -> QueryBuilder
+ Database.PostgreSQL.Store.Table: class (Entity a) => ColumnEntity a
+ Database.PostgreSQL.Store.Table: class GColumns (rec :: KColumns)
+ Database.PostgreSQL.Store.Table: class GTable (tbl :: KTable)
+ Database.PostgreSQL.Store.Table: class (Entity a) => TableEntity a where describeTableType = describeGenericTable
+ Database.PostgreSQL.Store.Table: data Column
+ Database.PostgreSQL.Store.Table: data ColumnType
+ Database.PostgreSQL.Store.Table: data KColumns
+ Database.PostgreSQL.Store.Table: data KTable
+ Database.PostgreSQL.Store.Table: data Table
+ Database.PostgreSQL.Store.Table: describeColumnType :: ColumnEntity a => proxy a -> ColumnType
+ Database.PostgreSQL.Store.Table: describeGenericTable :: (GenericTable a) => proxy a -> Table
+ Database.PostgreSQL.Store.Table: describeTableType :: (TableEntity a, GenericTable a) => proxy a -> Table
+ Database.PostgreSQL.Store.Table: gDescribeColumns :: GColumns rec => proxy rec -> [Column]
+ Database.PostgreSQL.Store.Table: gDescribeTable :: GTable tbl => proxy tbl -> Table
+ Database.PostgreSQL.Store.Table: insertColumns :: Table -> QueryBuilder
+ Database.PostgreSQL.Store.Table: insertColumnsOn :: Table -> ByteString -> QueryBuilder
+ Database.PostgreSQL.Store.Table: instance (Database.PostgreSQL.Store.Table.GColumns lhs, Database.PostgreSQL.Store.Table.GColumns rhs) => Database.PostgreSQL.Store.Table.GColumns ('Database.PostgreSQL.Store.Table.TCombine lhs rhs)
+ Database.PostgreSQL.Store.Table: instance (GHC.TypeLits.KnownSymbol name, Database.PostgreSQL.Store.ColumnEntity.ColumnEntity typ) => Database.PostgreSQL.Store.Table.GColumns ('Database.PostgreSQL.Store.Table.TSelector name typ)
+ Database.PostgreSQL.Store.Table: instance (GHC.TypeLits.KnownSymbol name, Database.PostgreSQL.Store.Table.GColumns cols) => Database.PostgreSQL.Store.Table.GTable ('Database.PostgreSQL.Store.Table.TTable name cols)
+ Database.PostgreSQL.Store.Table: type AnalyzeTable a = AnalyzeTableRep a (Rep a)
+ Database.PostgreSQL.Store.Table: type GenericTable a = (Generic a, GTable (AnalyzeTable a))
+ Database.PostgreSQL.Store.Types: Query :: ByteString -> [TypedValue] -> Query a
+ Database.PostgreSQL.Store.Types: TypedValue :: Oid -> (Maybe Value) -> TypedValue
+ Database.PostgreSQL.Store.Types: Value :: ByteString -> Value
+ Database.PostgreSQL.Store.Types: [queryParams] :: Query a -> [TypedValue]
+ Database.PostgreSQL.Store.Types: [queryStatement] :: Query a -> ByteString
+ Database.PostgreSQL.Store.Types: [valueData] :: Value -> ByteString
+ Database.PostgreSQL.Store.Types: data Query a
+ Database.PostgreSQL.Store.Types: data TypedValue
+ Database.PostgreSQL.Store.Types: instance GHC.Classes.Eq (Database.PostgreSQL.Store.Types.Query a)
+ Database.PostgreSQL.Store.Types: instance GHC.Classes.Eq Database.PostgreSQL.Store.Types.TypedValue
+ Database.PostgreSQL.Store.Types: instance GHC.Classes.Eq Database.PostgreSQL.Store.Types.Value
+ Database.PostgreSQL.Store.Types: instance GHC.Classes.Ord (Database.PostgreSQL.Store.Types.Query a)
+ Database.PostgreSQL.Store.Types: instance GHC.Classes.Ord Database.PostgreSQL.Store.Types.TypedValue
+ Database.PostgreSQL.Store.Types: instance GHC.Classes.Ord Database.PostgreSQL.Store.Types.Value
+ Database.PostgreSQL.Store.Types: instance GHC.Show.Show (Database.PostgreSQL.Store.Types.Query a)
+ Database.PostgreSQL.Store.Types: instance GHC.Show.Show Database.PostgreSQL.Store.Types.TypedValue
+ Database.PostgreSQL.Store.Types: instance GHC.Show.Show Database.PostgreSQL.Store.Types.Value
+ Database.PostgreSQL.Store.Types: newtype Value
- Database.PostgreSQL.Store: Query :: !ByteString -> ![Value] -> Query
+ Database.PostgreSQL.Store: Query :: ByteString -> [TypedValue] -> Query a
- Database.PostgreSQL.Store: [queryParams] :: Query -> ![Value]
+ Database.PostgreSQL.Store: [queryParams] :: Query a -> [TypedValue]
- Database.PostgreSQL.Store: [queryStatement] :: Query -> !ByteString
+ Database.PostgreSQL.Store: [queryStatement] :: Query a -> ByteString
- Database.PostgreSQL.Store: data Query
+ Database.PostgreSQL.Store: data Query a
- Database.PostgreSQL.Store: execute :: Query -> Errand Result
+ Database.PostgreSQL.Store: execute :: Query a -> Errand Result
- Database.PostgreSQL.Store: insert :: Table a => a -> Errand (Reference a)
+ Database.PostgreSQL.Store: insert :: (TableEntity a) => a -> Errand Bool
- Database.PostgreSQL.Store: insertMany :: Table a => [a] -> Errand [Reference a]
+ Database.PostgreSQL.Store: insertMany :: (TableEntity a) => [a] -> Errand Int
- Database.PostgreSQL.Store: query :: (Result a) => Query -> Errand [a]
+ Database.PostgreSQL.Store: query :: (Entity a) => Query a -> Errand [a]
- Database.PostgreSQL.Store: queryWith :: Query -> ResultProcessor a -> Errand [a]
+ Database.PostgreSQL.Store: queryWith :: Query a -> RowParser a -> Errand [a]
- Database.PostgreSQL.Store.Errand: execute :: Query -> Errand Result
+ Database.PostgreSQL.Store.Errand: execute :: Query a -> Errand Result
- Database.PostgreSQL.Store.Errand: query :: (Result a) => Query -> Errand [a]
+ Database.PostgreSQL.Store.Errand: query :: (Entity a) => Query a -> Errand [a]
- Database.PostgreSQL.Store.Errand: queryWith :: Query -> ResultProcessor a -> Errand [a]
+ Database.PostgreSQL.Store.Errand: queryWith :: Query a -> RowParser a -> Errand [a]

Files

pg-store.cabal view
@@ -1,5 +1,5 @@ name:                pg-store-version:             0.1.1+version:             0.2 category:            Database synopsis:            Simple storage interface to PostgreSQL description:         Simple storage interface to PostgreSQL@@ -21,24 +21,18 @@   default-language: Haskell2010   build-depends:    base >= 4.9 && < 5,                     template-haskell >= 2.11 && < 3,-                    bytestring, text, postgresql-libpq, attoparsec, mtl, time+                    bytestring, blaze-builder, text, postgresql-libpq, attoparsec, mtl, time,+                    haskell-src-meta, aeson, scientific   hs-source-dirs:   src-  exposed-modules:  Database.PostgreSQL.Store.Result,-                    Database.PostgreSQL.Store.Columns,-                    Database.PostgreSQL.Store.Query,-                    Database.PostgreSQL.Store.Errand,-                    Database.PostgreSQL.Store.Table,-                    Database.PostgreSQL.Store.OIDs,+  exposed-modules:  Database.PostgreSQL.Store.RowParser+                    Database.PostgreSQL.Store.Errand+                    Database.PostgreSQL.Store.Query.Builder+                    Database.PostgreSQL.Store.Query.TH+                    Database.PostgreSQL.Store.Query+                    Database.PostgreSQL.Store.Types+                    Database.PostgreSQL.Store.Generics+                    Database.PostgreSQL.Store.Entity+                    Database.PostgreSQL.Store.ColumnEntity+                    Database.PostgreSQL.Store.Table                     Database.PostgreSQL.Store--test-suite tests-  type:             exitcode-stdio-1.0-  ghc-options:      -Wall -fno-warn-unused-do-bind -fno-warn-tabs -fno-warn-name-shadowing-  default-language: Haskell2010-  build-depends:    base, pg-store, hspec, QuickCheck, bytestring, text, postgresql-libpq, mtl-  hs-source-dirs:   tests-  other-modules:    Test.Database.PostgreSQL.Store.Query,-                    Test.Database.PostgreSQL.Store.Columns,-                    Test.Database.PostgreSQL.Store.Table,-                    Test.Database.PostgreSQL.Store-  main-is:          Main.hs+  other-modules:    Database.PostgreSQL.Store.Utilities
src/Database/PostgreSQL/Store.hs view
@@ -1,52 +1,54 @@ -- | -- Module:     Database.PostgreSQL.Store--- Copyright:  (c) Ole Krüger 2015-2016+-- Copyright:  (c) Ole Krüger 2016 -- License:    BSD3 -- Maintainer: Ole Krüger <ole@vprsm.de> module Database.PostgreSQL.Store (-	-- * Errands+	-- * Errand 	Errand,-	ErrandError (..),-	ErrorCode (..),-	ExecStatus (..), 	runErrand,+ 	execute,+	execute', 	query,-	query_, 	queryWith, -	-- * Queries+	insert,+	insertMany,+	deleteAll,+	findAll,+	create,++	-- * Query 	Query (..), 	pgsq,-	pgss,--	QueryTable (..),-	SelectorElement (..),--	-- * Values-	Value (..),-	Column (..),--	-- * Results-	Result (..),-	ResultProcessor,-	ResultError (..),-	skipColumn,-	unpackColumn,+	castQuery, -	Single (..),-	Reference (..),+	-- * Entity+	Entity (..),  	-- * Tables+	TableEntity (..),+	ColumnEntity (..),+ 	Table (..),-	mkCreateQuery,+	ColumnType (..),+	Column (..), -	mkTable,-	TableConstraint (..)+	-- * Errors+	ErrandError (..),+	ErrorCode (..),+	P.ExecStatus,+	RowError (..),+	RowErrorLocation (..),+	RowErrorDetail (..) ) where -import Database.PostgreSQL.Store.Columns-import Database.PostgreSQL.Store.Errand-import Database.PostgreSQL.Store.Query-import Database.PostgreSQL.Store.Result-import Database.PostgreSQL.Store.Table+import           Database.PostgreSQL.Store.Types+import           Database.PostgreSQL.Store.Query+import           Database.PostgreSQL.Store.Table+import           Database.PostgreSQL.Store.Entity+import           Database.PostgreSQL.Store.RowParser+import           Database.PostgreSQL.Store.Errand++import qualified Database.PostgreSQL.LibPQ as P
+ src/Database/PostgreSQL/Store/ColumnEntity.hs view
@@ -0,0 +1,188 @@+{-# LANGUAGE OverloadedStrings, FlexibleInstances, ScopedTypeVariables, GeneralizedNewtypeDeriving #-}++-- |+-- Module:     Database.PostgreSQL.Store.ColumnEntity+-- Copyright:  (c) Ole Krüger 2016+-- License:    BSD3+-- Maintainer: Ole Krüger <ole@vprsm.de>+module Database.PostgreSQL.Store.ColumnEntity (+	ColumnType (..),+	ColumnEntity (..)+) where++import           Data.Proxy++import           Data.Int+import           Data.Word+import           Data.Scientific+import           Numeric.Natural++import qualified Data.Aeson           as A++import qualified Data.ByteString      as B+import qualified Data.ByteString.Lazy as BL+import qualified Data.Text            as T+import qualified Data.Text.Lazy       as TL++import           Database.PostgreSQL.Store.Entity+import           Database.PostgreSQL.Store.Utilities+import           Database.PostgreSQL.Store.Query.Builder++-- | Description of a column type+data ColumnType = ColumnType {+	-- | Type name+	colTypeName :: B.ByteString,++	-- | @NOT NULL@ constraint present?+	colTypeNotNull :: Bool,++	-- | Produce a check statement body when given a column name+	colTypeCheck :: Maybe (B.ByteString -> QueryBuilder)+}++-- | Classify a type which can be used as a column in a table.+class (Entity a) => ColumnEntity a where+	-- | Describe the column type+	describeColumnType :: proxy a -> ColumnType++instance (ColumnEntity a) => ColumnEntity (Maybe a) where+	describeColumnType _ =+		(describeColumnType (Proxy :: Proxy a)) {+			colTypeNotNull = False+		}++newtype PGInt2 = PGInt2 Int16+	deriving (Show, Read, Eq, Ord, Enum, Bounded, Integral, Num, Real)++instance Entity PGInt2 where+	insertEntity (PGInt2 x) =+		insertEntity x++	parseEntity =+		PGInt2 <$> parseEntity++instance ColumnEntity PGInt2 where+	describeColumnType _ =+		ColumnType "int2" True Nothing++newtype PGInt4 = PGInt4 Int32+	deriving (Show, Read, Eq, Ord, Enum, Bounded, Integral, Num, Real)++instance Entity PGInt4 where+	insertEntity (PGInt4 x) =+		insertEntity x++	parseEntity =+		PGInt4 <$> parseEntity++instance ColumnEntity PGInt4 where+	describeColumnType _ =+		ColumnType "int4" True Nothing++newtype PGInt8 = PGInt8 Int64+	deriving (Show, Read, Eq, Ord, Enum, Bounded, Integral, Num, Real)++instance Entity PGInt8 where+	insertEntity (PGInt8 x) =+		insertEntity x++	parseEntity =+		PGInt8 <$> parseEntity++instance ColumnEntity PGInt8 where+	describeColumnType _ =+		ColumnType "int8" True Nothing++-- | Select a type which can contain the given numeric type.+selectBestColumnType :: (Show a, Num a, Ord a, Bounded a) => proxy a -> ColumnType+selectBestColumnType proxy+	| -32768 <= lower && upper <= 32767 =+		ColumnType "int2" True Nothing+	| -2147483648 <= lower && upper <= 2147483647 =+		ColumnType "int4" True Nothing+	| -9223372036854775808 <= lower && upper <= 9223372036854775807 =+		ColumnType "int8" True Nothing+	| otherwise =+		ColumnType (buildByteString ("numeric(" ++ show digits ++ ",0)")) True Nothing+	where+		upper = (const maxBound :: (Bounded a) => proxy a -> a) proxy+		lower = (const minBound :: (Bounded a) => proxy a -> a) proxy+		digits = max (length (show upper)) (length (show lower))++instance ColumnEntity Bool where+	describeColumnType _ =+		ColumnType "bool" True Nothing++instance ColumnEntity Integer where+	describeColumnType _ =+		ColumnType "numeric" True Nothing++instance ColumnEntity Int where+	describeColumnType = selectBestColumnType++instance ColumnEntity Int8 where+	describeColumnType = selectBestColumnType++instance ColumnEntity Int16 where+	describeColumnType = selectBestColumnType++instance ColumnEntity Int32 where+	describeColumnType = selectBestColumnType++instance ColumnEntity Int64 where+	describeColumnType = selectBestColumnType++instance ColumnEntity Natural where+	describeColumnType _ =+		ColumnType "numeric" True Nothing++instance ColumnEntity Word where+	describeColumnType = selectBestColumnType++instance ColumnEntity Word8 where+	describeColumnType = selectBestColumnType++instance ColumnEntity Word16 where+	describeColumnType = selectBestColumnType++instance ColumnEntity Word32 where+	describeColumnType = selectBestColumnType++instance ColumnEntity Word64 where+	describeColumnType = selectBestColumnType++instance ColumnEntity Float where+	describeColumnType _ =+		ColumnType "real" True Nothing++instance ColumnEntity Double where+	describeColumnType _ =+		ColumnType "double precision" True Nothing++instance ColumnEntity Scientific where+	describeColumnType _ =+		ColumnType "numeric" True Nothing++instance ColumnEntity String where+	describeColumnType _ =+		ColumnType "text" True Nothing++instance ColumnEntity T.Text where+	describeColumnType _ =+		ColumnType "text" True Nothing++instance ColumnEntity TL.Text where+	describeColumnType _ =+		ColumnType "text" True Nothing++instance ColumnEntity B.ByteString where+	describeColumnType _ =+		ColumnType "bytea" True Nothing++instance ColumnEntity BL.ByteString where+	describeColumnType _ =+		ColumnType "bytea" True Nothing++instance ColumnEntity A.Value where+	describeColumnType _ =+		ColumnType "json" True Nothing
− src/Database/PostgreSQL/Store/Columns.hs
@@ -1,369 +0,0 @@-{-# LANGUAGE OverloadedStrings, RecordWildCards, FlexibleInstances, TemplateHaskell #-}---- |--- Module:     Database.PostgreSQL.Store.Columns--- Copyright:  (c) Ole Krüger 2015-2016--- License:    BSD3--- Maintainer: Ole Krüger <ole@vprsm.de>-module Database.PostgreSQL.Store.Columns (-	-- *-	Value (..),--	-- *-	Column (..)-) where--import           Data.Int-import           Data.Word-import           Data.Bits-import           Data.Time-import           Data.Monoid-import           Data.Typeable-import qualified Data.Text                      as T-import qualified Data.Text.Encoding             as T-import qualified Data.Text.Lazy                 as TL-import qualified Data.ByteString                as B-import qualified Data.ByteString.Char8          as C8-import qualified Data.ByteString.Lazy           as BL-import           Data.ByteString.Builder-import           Data.Attoparsec.ByteString-import           Data.Attoparsec.ByteString.Char8 (signed, decimal)--import           Database.PostgreSQL.LibPQ (Oid)-import qualified Database.PostgreSQL.Store.OIDs as OID---- | Query parameter or value of a column - see 'pack' on how to generate 'Value's manually but---   conveniently.-data Value-	= Value {-		-- | Type object identifier-		valueType :: Oid,--		-- | Data value-		valueData :: B.ByteString-	}-	| NullValue-	deriving (Show, Eq, Ord)---- | Types which implement this type class may be used as column types.-class Column a where-	-- | Pack column value.-	pack :: a -> Value--	-- | Unpack column value.-	unpack :: Value -> Maybe a--	-- | Name of the underlying SQL type.-	columnTypeName :: Proxy a -> String--	-- | May the column be NULL?-	columnAllowNull :: Proxy a -> Bool-	columnAllowNull _proxy = False--	-- | A condition that must hold true for the column.-	columnCheck :: Proxy a -> String -> Maybe String-	columnCheck _proxy _identifier = Nothing--	-- | Generate column description in SQL. Think @CREATE TABLE@.-	columnDescription :: Proxy a -> String -> String-	columnDescription proxy identifier =-		identifier ++ " " ++-		columnTypeName proxy ++-		if columnAllowNull proxy then "" else " NOT NULL" ++-		case columnCheck proxy identifier of-			Just stmt -> " CHECK (" ++ stmt ++ ")"-			Nothing   -> ""--instance Column Value where-	pack = id-	unpack = Just-	columnTypeName _ = "blob"--instance (Column a) => Column (Maybe a) where-	pack = maybe NullValue pack--	unpack NullValue = Just Nothing-	unpack val       = Just <$> unpack val--	columnTypeName proxy = columnTypeName ((const Proxy :: Proxy (Maybe b) -> Proxy b) proxy)-	columnAllowNull _    = True-	columnCheck proxy    = columnCheck ((const Proxy :: Proxy (Maybe b) -> Proxy b) proxy)--instance Column Bool where-	pack True = Value $(OID.bool) "true"-	pack _    = Value $(OID.bool) "false"--	unpack (Value $(OID.bool) "true") = Just True-	unpack (Value $(OID.bool) "TRUE") = Just True-	unpack (Value $(OID.bool) "t"   ) = Just True-	unpack (Value $(OID.bool) "y"   ) = Just True-	unpack (Value $(OID.bool) "yes" ) = Just True-	unpack (Value $(OID.bool) "YES" ) = Just True-	unpack (Value $(OID.bool) "on"  ) = Just True-	unpack (Value $(OID.bool) "ON"  ) = Just True-	unpack (Value $(OID.bool) "1"   ) = Just True-	unpack (Value $(OID.bool) _     ) = Just False-	unpack _                          = Nothing--	columnTypeName _ = "bool"--instance Column Int where-	pack n = Value $(OID.int8) (buildByteString intDec n)--	unpack (Value $(OID.int2) dat) = parseMaybe (signed decimal) dat-	unpack (Value $(OID.int4) dat) = parseMaybe (signed decimal) dat-	unpack (Value $(OID.int8) dat) = parseMaybe (signed decimal) dat-	unpack _                       = Nothing--	columnTypeName _ = "int8"--	columnCheck _ nm =-		Just (nm ++ " >= " ++ show (minBound :: Int) ++-		      " AND " ++-		      nm ++ " <= " ++ show (maxBound :: Int))--instance Column Int8 where-	pack n = Value $(OID.int2) (buildByteString int8Dec n)--	unpack (Value $(OID.int2) dat) = parseMaybe (signed decimal) dat-	unpack (Value $(OID.int4) dat) = parseMaybe (signed decimal) dat-	unpack (Value $(OID.int8) dat) = parseMaybe (signed decimal) dat-	unpack _                       = Nothing--	columnTypeName _ = "int2"--instance Column Int16 where-	pack n = Value $(OID.int2) (buildByteString int16Dec n)--	unpack (Value $(OID.int2) dat) = parseMaybe (signed decimal) dat-	unpack (Value $(OID.int4) dat) = parseMaybe (signed decimal) dat-	unpack (Value $(OID.int8) dat) = parseMaybe (signed decimal) dat-	unpack _                       = Nothing--	columnTypeName _ = "int2"--instance Column Int32 where-	pack n = Value $(OID.int4) (buildByteString int32Dec n)--	unpack (Value $(OID.int2) dat) = parseMaybe (signed decimal) dat-	unpack (Value $(OID.int4) dat) = parseMaybe (signed decimal) dat-	unpack (Value $(OID.int8) dat) = parseMaybe (signed decimal) dat-	unpack _                       = Nothing---	columnTypeName _ = "int4"--instance Column Int64 where-	pack n = Value $(OID.int8) (buildByteString int64Dec n)--	unpack (Value $(OID.int2) dat) = parseMaybe (signed decimal) dat-	unpack (Value $(OID.int4) dat) = parseMaybe (signed decimal) dat-	unpack (Value $(OID.int8) dat) = parseMaybe (signed decimal) dat-	unpack _                       = Nothing--	columnTypeName _ = "int8"---- | Does "Word" require to be stored in type "numeric"?-wordRequiresNumeric :: Bool-wordRequiresNumeric =-	-- int8 upper bound is 2^63 - 1 (9223372036854775807)-	(fromIntegral (maxBound :: Word) :: Integer) > 9223372036854775807--instance Column Word where-	pack n | wordRequiresNumeric = Value $(OID.numeric) (buildByteString wordDec n)-	       | otherwise           = Value $(OID.int8)    (buildByteString wordDec n)--	unpack (Value $(OID.int2)    dat) = parseMaybe decimal dat-	unpack (Value $(OID.int4)    dat) = parseMaybe decimal dat-	unpack (Value $(OID.int8)    dat) = parseMaybe decimal dat-	unpack (Value $(OID.numeric) dat) = parseMaybe decimal dat-	unpack _                          = Nothing--	columnTypeName _ = if wordRequiresNumeric then "numeric(20, 0)" else "int8"--	columnCheck _ nm =-		Just (nm ++ " >= 0 AND " ++ nm ++ " <= " ++ show (maxBound :: Word))--instance Column Word8 where-	pack n = Value $(OID.int2) (buildByteString word8Dec n)--	unpack (Value $(OID.int2) dat) = parseMaybe decimal dat-	unpack (Value $(OID.int4) dat) = parseMaybe decimal dat-	unpack (Value $(OID.int8) dat) = parseMaybe decimal dat-	unpack _                       = Nothing--	columnTypeName _ = "int2"--	columnCheck _ nm =-		Just (nm ++ " >= 0 AND " ++ nm ++ " <= " ++ show (maxBound :: Word8))--instance Column Word16 where-	pack n = Value $(OID.int4) (buildByteString word16Dec n)--	unpack (Value $(OID.int2) dat) = parseMaybe decimal dat-	unpack (Value $(OID.int4) dat) = parseMaybe decimal dat-	unpack (Value $(OID.int8) dat) = parseMaybe decimal dat-	unpack _                       = Nothing--	columnTypeName _ = "int4"--	columnCheck _ nm =-		Just (nm ++ " >= 0 AND " ++ nm ++ " <= " ++ show (maxBound :: Word16))--instance Column Word32 where-	pack n = Value $(OID.int8) (buildByteString word32Dec n)--	unpack (Value $(OID.int2) dat) = parseMaybe decimal dat-	unpack (Value $(OID.int4) dat) = parseMaybe decimal dat-	unpack (Value $(OID.int8) dat) = parseMaybe decimal dat-	unpack _                       = Nothing--	columnTypeName _ = "bigint"--	columnCheck _ nm =-		Just (nm ++ " >= 0 AND " ++ nm ++ " <= " ++ show (maxBound :: Word32))--instance Column Word64 where-	pack n = Value $(OID.numeric) (buildByteString word64Dec n)--	unpack (Value $(OID.int2)    dat) = parseMaybe decimal dat-	unpack (Value $(OID.int4)    dat) = parseMaybe decimal dat-	unpack (Value $(OID.int8)    dat) = parseMaybe decimal dat-	unpack (Value $(OID.numeric) dat) = parseMaybe decimal dat-	unpack _                          = Nothing--	columnTypeName _ = "numeric(20, 0)"--	columnCheck _ nm =-		Just (nm ++ " >= 0 AND " ++ nm ++ " <= " ++ show (maxBound :: Word64))--instance Column Integer where-	pack n = Value $(OID.numeric) (buildByteString integerDec n)--	unpack (Value $(OID.int2)    dat) = parseMaybe decimal dat-	unpack (Value $(OID.int4)    dat) = parseMaybe decimal dat-	unpack (Value $(OID.int8)    dat) = parseMaybe decimal dat-	unpack (Value $(OID.numeric) dat) = parseMaybe decimal dat-	unpack _                          = Nothing--	columnTypeName _ = "numeric"--instance Column UTCTime where-	pack t = Value $(OID.timestamp) (C8.pack (formatTime defaultTimeLocale "%F %T%Q" t))--	unpack (Value $(OID.timestamp) dat) =-		parseTimeM False defaultTimeLocale "%F %T%Q" (C8.unpack dat)-	unpack _ = Nothing--	columnTypeName _ = "timestamp"--instance Column [Char] where-	pack str = Value $(OID.text) (buildByteString stringUtf8 str)--	unpack (Value $(OID.varchar) dat) = pure (T.unpack (T.decodeUtf8 dat))-	unpack (Value $(OID.char)    dat) = pure (T.unpack (T.decodeUtf8 dat))-	unpack (Value $(OID.text)    dat) = pure (T.unpack (T.decodeUtf8 dat))-	unpack _                          = Nothing--	columnTypeName _ = "text"--instance Column T.Text where-	pack txt = Value $(OID.text) (T.encodeUtf8 txt)--	unpack (Value $(OID.varchar) dat) = pure (T.decodeUtf8 dat)-	unpack (Value $(OID.char)    dat) = pure (T.decodeUtf8 dat)-	unpack (Value $(OID.text)    dat) = pure (T.decodeUtf8 dat)-	unpack _                          = Nothing--	columnTypeName _ = "text"--instance Column TL.Text where-	pack txt = pack (TL.toStrict txt)--	unpack val = TL.fromStrict <$> unpack val--	columnTypeName _  = columnTypeName (Proxy :: Proxy T.Text)-	columnAllowNull _ = columnAllowNull (Proxy :: Proxy T.Text)-	columnCheck _     = columnCheck (Proxy :: Proxy T.Text)--instance Column B.ByteString where-	pack bs = Value $(OID.bytea) (encodeByteaHex bs)--	unpack (Value $(OID.varchar) dat) = pure dat-	unpack (Value $(OID.char)    dat) = pure dat-	unpack (Value $(OID.text)    dat) = pure dat-	unpack (Value $(OID.bytea)   dat) = decodeByteaHex dat-	unpack _                          = Nothing--	columnTypeName _ = "bytea"--instance Column BL.ByteString where-	pack bs = pack (BL.toStrict bs)--	unpack val = BL.fromStrict <$> unpack val--	columnTypeName _  = columnTypeName (Proxy :: Proxy B.ByteString)-	columnAllowNull _ = columnAllowNull (Proxy :: Proxy B.ByteString)-	columnCheck _     = columnCheck (Proxy :: Proxy B.ByteString)---- | Produce the two-digit hexadecimal representation of a 8-bit word.-word8ToHex :: Word8 -> B.ByteString-word8ToHex w =-	hex (shiftR w 4) <> hex (w .&. 15)-	where-		hex n =-			-- lel-			case n of {-				15 -> "F"; 14 -> "E"; 13 -> "D"; 12 -> "C"; 11 -> "B";-				10 -> "A"; 9  -> "9"; 8  -> "8"; 7  -> "7"; 6  -> "6";-				5  -> "5"; 4  -> "4"; 3  -> "3"; 2  -> "2"; 1  -> "1";-				_  -> "0"-			}---- | Retrieve 8-bit word from two-digit hexadecimal representation.-hexToWord8 :: B.ByteString -> Word8-hexToWord8 bs =-	case B.unpack bs of-		(a : b : _) -> shiftL (unhex a) 4 .|. unhex b-		(a : _) -> unhex a-		_ -> 0-	where-		unhex n =-			-- double lel-			case n of {-				48  ->  0; 49  ->  1; 50 ->  2; 51 ->  3; 52  ->  4;-				53  ->  5; 54  ->  6; 55 ->  7; 56 ->  8; 57  ->  9;-				65  -> 10; 66  -> 11; 67 -> 12; 68 -> 13; 69  -> 14;-				70  -> 15; 97  -> 10; 98 -> 11; 99 -> 12; 100 -> 13;-				101 -> 14; 102 -> 15; _  ->  0-			}---- | Unpack a byte array in textual representation.-decodeByteaHex :: B.ByteString -> Maybe B.ByteString-decodeByteaHex bs-	| B.length bs >= 2 && mod (B.length bs) 2 == 0 && B.isPrefixOf "\\x" bs =-		Just (B.pack (unfoldHex (B.drop 2 bs)))-	| otherwise = Nothing-		where-			unfoldHex "" = []-			unfoldHex bs = hexToWord8 (B.take 2 bs) : unfoldHex (B.drop 2 bs)---- | Pack textual representation of a byte array.-encodeByteaHex :: B.ByteString -> B.ByteString-encodeByteaHex bs =-	"\\x" <> B.concatMap word8ToHex bs---- | Finish the parsing process.-finishParser :: Result r -> Result r-finishParser (Partial f) = f B.empty-finishParser x = x---- | Parse a ByteString.-parseMaybe :: Parser a -> B.ByteString -> Maybe a-parseMaybe p i =-	maybeResult (finishParser (parse p i))---- | Build strict ByteString.-buildByteString :: (a -> Builder) -> a -> B.ByteString-buildByteString f x =-	BL.toStrict (toLazyByteString (f x))
+ src/Database/PostgreSQL/Store/Entity.hs view
@@ -0,0 +1,418 @@+{-# LANGUAGE OverloadedStrings,+             ConstraintKinds,+             DataKinds,+             DefaultSignatures,+             FlexibleContexts,+             FlexibleInstances,+             ScopedTypeVariables,+             TypeFamilies,+             TypeOperators,+             TypeSynonymInstances,+             UndecidableInstances+#-}++-- |+-- Module:     Database.PostgreSQL.Store.Entity+-- Copyright:  (c) Ole Krüger 2016+-- License:    BSD3+-- Maintainer: Ole Krüger <ole@vprsm.de>+module Database.PostgreSQL.Store.Entity (+	-- * Result and query entity+	Entity (..),++	insertGeneric,+	parseGeneric,++	-- * Helpers+	GEntityRecord (..),+	GEntityEnum (..),+	GEntity (..)+) where++import           GHC.Generics+import           GHC.TypeLits++import           Control.Applicative+import           Control.Monad++import           Data.Int+import           Data.Bits+import           Data.Word+import           Data.Scientific hiding (scientific)+import           Numeric+import           Numeric.Natural++import           Data.Bifunctor+import           Data.Proxy++import qualified Data.Aeson              as A+import qualified Data.ByteString         as B+import qualified Data.ByteString.Lazy    as BL+import qualified Data.Text               as T+import qualified Data.Text.Encoding      as T+import qualified Data.Text.Lazy          as TL+import qualified Data.Text.Lazy.Encoding as TL++import           Data.Attoparsec.ByteString+import           Data.Attoparsec.ByteString.Char8 (signed, decimal, skipSpace, double, scientific)++import           Database.PostgreSQL.Store.Types+import           Database.PostgreSQL.Store.Utilities+import           Database.PostgreSQL.Store.Generics+import           Database.PostgreSQL.Store.Query.Builder+import           Database.PostgreSQL.Store.RowParser++import           Database.PostgreSQL.LibPQ (Oid (..), invalidOid)++-- | Generic record entity+class GEntityRecord (rec :: KRecord) where+	gInsertRecord :: Record rec -> QueryBuilder++	gParseRecord :: RowParser (Record rec)++instance (Entity typ) => GEntityRecord ('TSingle meta typ) where+	gInsertRecord (Single x) =+		insertEntity x++	gParseRecord =+		Single <$> parseEntity++instance (GEntityRecord lhs, GEntityRecord rhs) => GEntityRecord ('TCombine lhs rhs) where+	gInsertRecord (Combine lhs rhs) = do+		gInsertRecord lhs+		insertCode ","+		gInsertRecord rhs++	gParseRecord =+		Combine <$> gParseRecord <*> gParseRecord++-- | Generic enumeration entity+class GEntityEnum (enum :: KFlatSum) where+	gInsertEnum :: FlatSum enum -> QueryBuilder++	gEnumValues :: [(B.ByteString, FlatSum enum)]++instance (KnownSymbol name) => GEntityEnum ('TValue ('MetaCons name f r)) where+	gInsertEnum _ =+		insertQuote (buildByteString (symbolVal (Proxy :: Proxy name)))++	gEnumValues =+		[(buildByteString (symbolVal (Proxy :: Proxy name)), Unit)]++instance (GEntityEnum lhs, GEntityEnum rhs) => GEntityEnum ('TChoose lhs rhs) where+	gInsertEnum (ChooseLeft lhs)  = gInsertEnum lhs+	gInsertEnum (ChooseRight rhs) = gInsertEnum rhs++	gEnumValues =+		map (second ChooseLeft) gEnumValues+		++ map (second ChooseRight) gEnumValues++-- | Generic entity+class GEntity (dat :: KDataType) where+	gInsertEntity :: DataType dat -> QueryBuilder++	gParseEntity :: RowParser (DataType dat)++instance (GEntityRecord rec) => GEntity ('TRecord d c rec) where+	gInsertEntity (Record x) =+		gInsertRecord x++	gParseEntity =+		Record <$> gParseRecord++instance (GEntityEnum enum) => GEntity ('TFlatSum d enum) where+	gInsertEntity (FlatSum x) =+		gInsertEnum x++	gParseEntity =+		FlatSum <$> parseContents (`lookup` gEnumValues)++-- | Insert generic entity into the query.+insertGeneric :: (GenericEntity a, GEntity (AnalyzeEntity a)) => a -> QueryBuilder+insertGeneric x =+	gInsertEntity (fromGenericEntity x)++-- | Generic 'RowParser' for an entity.+parseGeneric :: (GenericEntity a, GEntity (AnalyzeEntity a)) => RowParser a+parseGeneric =+	toGenericEntity <$> gParseEntity++-- | An entity that is used as a parameter or result of a query.+class Entity a where+	-- | Insert an instance of @a@ into the query.+	insertEntity :: a -> QueryBuilder++	default insertEntity :: (GenericEntity a, GEntity (AnalyzeEntity a)) => a -> QueryBuilder+	insertEntity = insertGeneric++	-- | Retrieve an instance of @a@ from the result set.+	parseEntity :: RowParser a++	default parseEntity :: (GenericEntity a, GEntity (AnalyzeEntity a)) => RowParser a+	parseEntity = parseGeneric++-- | 2 result entities in sequence+instance (Entity a, Entity b) => Entity (a, b)++-- | 3 result entities in sequence+instance (Entity a, Entity b, Entity c) => Entity (a, b, c)++-- | 4 result entities in sequence+instance (Entity a, Entity b, Entity c, Entity d) => Entity (a, b, c, d)++-- | 5 result entities in sequence+instance (Entity a, Entity b, Entity c, Entity d, Entity e) => Entity (a, b, c, d, e)++-- | 6 result entities in sequence+instance (Entity a, Entity b, Entity c, Entity d, Entity e, Entity f) => Entity (a, b, c, d, e, f)++-- | 7 result entities in sequence+instance (Entity a, Entity b, Entity c, Entity d, Entity e, Entity f, Entity g) => Entity (a, b, c, d, e, f, g)++-- | 'QueryBuilder'+instance Entity QueryBuilder where+	insertEntity = id++	parseEntity = do+		colsLeft <- columnsLeft+		insertCommaSeperated <$> replicateM (fromEnum colsLeft) (insertTypedValue <$> fetchColumn)++-- | Untyped column value+instance Entity Value where+	insertEntity = insertValue++	parseEntity = parseColumn (\ (TypedValue _ mbValue) -> mbValue)++-- | Typed column value+instance Entity TypedValue where+	insertEntity = insertTypedValue++	parseEntity = fetchColumn++-- | A value which may normally not be @NULL@.+instance (Entity a) => Entity (Maybe a) where+	insertEntity Nothing  = insertTypedValue (TypedValue invalidOid Nothing)+	insertEntity (Just x) = insertEntity x++	parseEntity = do+		TypedValue _ value <- peekColumn+		case value of+			Nothing -> pure Nothing+			_       -> Just <$> parseEntity+++-- | @boolean@+instance Entity Bool where+	insertEntity input =+		insertTypedValue (TypedValue (Oid 16) (Just (Value value)))+		where value | input = "t" | otherwise = "f"++	parseEntity =+		parseContents $ \ dat ->+			Just (elem dat ["t", "1", "true", "TRUE", "y", "yes", "YES", "on", "ON"])++-- | Parse a column using the given 'Parser'.+parseContentsWith :: Parser a -> RowParser a+parseContentsWith p =+	parseContents (maybeResult . endResult . parse p)+	where+		endResult (Partial f) = f B.empty+		endResult x           = x++-- | Simplified version of 'insertTypedValue'+insertTypedValue_ :: Oid -> B.ByteString -> QueryBuilder+insertTypedValue_ typ val =+	insertTypedValue (TypedValue typ (Just (Value val)))++-- | Insert a numeric value.+insertNumericValue :: (Show a) => a -> QueryBuilder+insertNumericValue x =+	insertTypedValue_ (Oid 1700) (showByteString x)++-- | Any integer+instance Entity Integer where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith (signed decimal)++-- | Any integer+instance Entity Int where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith (signed decimal)++-- | Any integer+instance Entity Int8 where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith (signed decimal)++-- | Any integer+instance Entity Int16 where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith (signed decimal)++-- | Any integer+instance Entity Int32 where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith (signed decimal)++-- | Any integer+instance Entity Int64 where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith (signed decimal)++-- | Any unsigned integer+instance Entity Natural where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith decimal++-- | Any unsigned integer+instance Entity Word where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith decimal++-- | Any unsigned integer+instance Entity Word8 where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith decimal++-- | Any unsigned integer+instance Entity Word16 where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith decimal++-- | Any unsigned integer+instance Entity Word32 where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith decimal++-- | Any unsigned integer+instance Entity Word64 where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith decimal++-- | Any floating-point number+instance Entity Double where+	insertEntity = insertNumericValue++	parseEntity = parseContentsWith double++-- | Any floating-point number+instance Entity Float where+	insertEntity = insertNumericValue++	parseEntity = (realToFrac :: Double -> Float) <$> parseEntity++-- | Any numeric type+instance Entity Scientific where+	insertEntity x =+		insertTypedValue_ (Oid 1700) (buildByteString (formatScientific Fixed Nothing x))++	parseEntity = parseContentsWith scientific++-- | @char@, @varchar@ or @text@ - UTF-8 encoded+instance Entity String where+	insertEntity value =+		insertTypedValue_ (Oid 25) (buildByteString value)++	parseEntity = T.unpack <$> parseEntity++-- | @char@, @varchar@ or @text@ - UTF-8 encoded+instance Entity T.Text where+	insertEntity value =+		insertTypedValue_ (Oid 25) (T.encodeUtf8 value)++	parseEntity =+		parseContents (either (const Nothing) Just . T.decodeUtf8')++-- | @char@, @varchar@ or @text@ - UTF-8 encoded+instance Entity TL.Text where+	insertEntity value =+		insertEntity (TL.toStrict value)++	parseEntity =+		parseContents (either (const Nothing) Just . TL.decodeUtf8' . BL.fromStrict)++-- | @bytea@ - byte array encoded in hex format+instance Entity B.ByteString where+	insertEntity value =+		insertTypedValue_ (Oid 17) dat+		where+			dat = B.append "\\x" (B.concatMap showHex' value)++			showHex' n =+				buildByteString $ case showHex n [] of+					(a : b : _) -> [a, b]+					(a : _)     -> ['0', a]+					[]          -> "00"++	parseEntity =+		parseContentsWith (hexFormat <|> escapedFormat)+		where+			isHexChar x =+				(x >= 48 && x <= 57)     -- 0 - 9+				|| (x >= 65 && x <= 70)  -- A - Z+				|| (x >= 97 && x <= 102) -- a - z++			hexCharToWord x+				| x >= 48 && x <= 57  = x - 48+				| x >= 65 && x <= 70  = x - 55+				| x >= 97 && x <= 102 = x - 87+				| otherwise           = 0++			hexWord = do+				skipSpace+				a <- satisfy isHexChar+				b <- satisfy isHexChar++				pure (shiftL (hexCharToWord a) 4 .|. hexCharToWord b)++			hexFormat = do+				word8 92  -- \+				word8 120 -- x+				B.pack <$> many hexWord <* skipSpace++			isOctChar x = x >= 48 && x <= 55++			octCharToWord x+				| isOctChar x = x - 48+				| otherwise   = 0++			escapedWord = do+				word8 92+				a <- satisfy isOctChar+				b <- satisfy isOctChar+				c <- satisfy isOctChar++				pure (shiftL (octCharToWord a) 6 .|. shiftL (octCharToWord b) 3 .|. c)++			escapedBackslash = do+				word8 92+				word8 92++			escapedFormat =+				B.pack <$> many (escapedBackslash <|> escapedWord <|> anyWord8)++-- | @bytea@ - byte array encoded in hex format+instance Entity BL.ByteString where+	insertEntity value =+		insertEntity (BL.toStrict value)++	parseEntity = BL.fromStrict <$> parseEntity++-- | @json@ or @jsonb@+instance Entity A.Value where+	insertEntity value =+		insertTypedValue_ (Oid 114) (BL.toStrict (A.encode value))++	parseEntity = parseContents A.decodeStrict
src/Database/PostgreSQL/Store/Errand.hs view
@@ -9,44 +9,49 @@ 	-- * Errand 	ErrandError (..), 	ErrorCode (..),-	P.ExecStatus (..),- 	Errand,+ 	runErrand,  	execute,+	execute', 	query,-	query_, 	queryWith, -	-- * Result parser-	Result (..),-	Single (..)+	insert,+	insertMany,+	deleteAll,+	findAll,+	create ) where  import           Control.Monad.Trans import           Control.Monad.Except import           Control.Monad.Reader +import           Data.Proxy import           Data.Maybe import qualified Data.ByteString           as B +import           Data.Attoparsec.ByteString.Char8+ import qualified Database.PostgreSQL.LibPQ as P-import           Database.PostgreSQL.Store.Query-import           Database.PostgreSQL.Store.Result-import           Database.PostgreSQL.Store.Columns +import           Database.PostgreSQL.Store.Types+import           Database.PostgreSQL.Store.Table+import           Database.PostgreSQL.Store.Entity+import           Database.PostgreSQL.Store.RowParser+import           Database.PostgreSQL.Store.Query.Builder+ -- | Error during errand data ErrandError 	= NoResult 		-- ^ No 'Result' has been returned.-	| EmptyResult-		-- ^ Result set is empty. 	| UserError String 		-- ^ A user has thrown an error. 	| ExecError P.ExecStatus ErrorCode B.ByteString B.ByteString B.ByteString 		-- ^ Query execution failed.-	| ResultError ResultError+	| ParseError RowError 		-- ^ Result processing failed. 	deriving (Show, Eq) @@ -72,15 +77,15 @@ 	runExceptT (runReaderT errand con)  -- | Execute a query and return the internal raw result.-execute :: Query -> Errand P.Result+execute :: Query a -> Errand P.Result execute (Query statement params) = Errand . ReaderT $ \ con -> do-	res <- ExceptT $-		transformResult <$> P.execParams con statement (map transformParam params) P.Text+	res <- ExceptT (transformResult <$> P.execParams con statement (map transformParam params) P.Text) 	status <- lift (P.resultStatus res)  	case status of-		P.CommandOk -> pure res-		P.TuplesOk  -> pure res+		P.CommandOk   -> pure res+		P.TuplesOk    -> pure res+		P.SingleTuple -> pure res  		other -> do 			(state, msg, detail, hint) <- lift $@@ -108,64 +113,94 @@  	where 		-- Turn 'Maybe P.Result' into 'Either ErrandError P.Result'-		transformResult = maybe (throwError NoResult) pure--		-- Turn 'Value' into 'Maybe (P.Oid, B.ByteString, P.Format)'-		transformParam (Value typ dat) = Just (typ, dat, P.Text)-		transformParam NullValue       = Nothing---- | Allows you to implement a custom result parser for your type.---   'mkTable' can implement this for your type.-class Result a where-	queryResultProcessor :: ResultProcessor a---- | Combine result parsers sequencially.-instance (Result a, Result b) => Result (a, b) where-	queryResultProcessor =-		(,) <$> queryResultProcessor <*> queryResultProcessor+		transformResult =+			maybe (throwError NoResult) pure -instance (Result a, Result b, Result c) => Result (a, b, c) where-	queryResultProcessor =-		(,,) <$> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor+		-- Turn 'TypedValue' into 'Maybe (P.Oid, B.ByteString, P.Format)'+		transformParam (TypedValue typ mbValue) =+			(\ (Value value) -> (typ, value, P.Text)) <$> mbValue -instance (Result a, Result b, Result c, Result d) => Result (a, b, c, d) where-	queryResultProcessor =-		(,,,) <$> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor+-- | Same as 'execute' but instead of a 'P.Result' it returns the number of affected rows.+execute' :: Query a -> Errand Int+execute' =+	countAffectedRows <=< execute -instance (Result a, Result b, Result c, Result d, Result e) => Result (a, b, c, d, e) where-	queryResultProcessor =-		(,,,,) <$> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor+-- | Execute a query and process its result set.+query :: (Entity a) => Query a -> Errand [a]+query qry =+	queryWith qry parseEntity -instance (Result a, Result b, Result c, Result d, Result e, Result f) => Result (a, b, c, d, e, f) where-	queryResultProcessor =-		(,,,,,) <$> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor+-- | Execute a query and process its result set using the provided 'RowParser'.+queryWith :: Query a -> RowParser a -> Errand [a]+queryWith qry parser = do+	result <- execute qry+	Errand (lift (withExceptT ParseError (parseResult result parser))) -instance (Result a, Result b, Result c, Result d, Result e, Result f, Result g) => Result (a, b, c, d, e, f, g) where-	queryResultProcessor =-		(,,,,,,) <$> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor <*> queryResultProcessor+-- | Counts the rows that have been affected by a query.+countAffectedRows :: P.Result -> Errand Int+countAffectedRows res = do+	fmap (\ numTuples -> fromMaybe 0 (numTuples >>= maybeResult . endResult . parse decimal))+	     (liftIO (P.cmdTuples res))+	where+		endResult (Partial f) = f B.empty+		endResult x           = x --- | Execute a query and process its result set.-query :: (Result a) => Query -> Errand [a]-query qry =-	queryWith qry queryResultProcessor+-- | Insert a row into a 'Table'.+insert :: (TableEntity a) => a -> Errand Bool+insert row = do+	fmap (> 0) . execute' $ buildQuery $ do+		insertCode "INSERT INTO "+		insertName name+		insertCode "("+		insertCommaSeperated (map (\ (Column colName _) -> insertName colName) cols)+		insertCode ") VALUES ("+		insertEntity row+		insertCode ")"+	where+		Table name cols =+			describeTableType ((const Proxy :: a -> Proxy a) row) --- | Execute a query and dismiss its result.-query_ :: Query -> Errand ()-query_ qry =-	() <$ execute qry+-- | Insert many rows into a 'Table'.+insertMany :: (TableEntity a) => [a] -> Errand Int+insertMany [] = pure 0+insertMany rows =+	execute' $ buildQuery $ do+		insertCode "INSERT INTO "+		insertName name+		insertCode "("+		insertCommaSeperated (map (\ (Column colName _) -> insertName colName) cols)+		insertCode ") VALUES "+		insertCommaSeperated (map insertRowValue rows)+	where+		Table name cols =+			describeTableType ((const Proxy :: [a] -> Proxy a) rows) --- | Execute a query and process its result set using the provided result processor.-queryWith :: Query -> ResultProcessor a -> Errand [a]-queryWith qry proc = do-	result <- execute qry-	Errand (lift (withExceptT ResultError (processResult result proc)))+		insertRowValue row = do+			insertCode "("+			insertEntity row+			insertCode ")" --- | Helper type to capture an single column.-newtype Single a = Single { fromSingle :: a }-	deriving (Eq, Ord)+-- | Delete all rows of a 'Table'.+deleteAll :: (TableEntity a) => proxy a -> Errand Int+deleteAll proxy =+	execute' $ buildQuery $ do+		insertCode "DELETE FROM "+		insertName (tableName (describeTableType proxy)) -instance (Show a) => Show (Single a) where-	show = show . fromSingle+-- | Find every row of a 'Table'.+findAll :: (TableEntity a) => Errand [a]+findAll =+	query (findAllQuery Proxy)+	where+		findAllQuery :: (TableEntity a) => Proxy a -> Query a+		findAllQuery proxy =+			buildQuery $ do+				insertCode "SELECT "+				insertColumns (describeTableType proxy)+				insertCode " FROM "+				insertName (tableName (describeTableType proxy)) -instance (Column a) => Result (Single a) where-	queryResultProcessor = Single <$> unpackColumn+-- | Create the given 'Table' type.+create :: (TableEntity a) => proxy a -> Errand ()+create proxy =+	() <$ execute (buildQuery (buildTableSchema (describeTableType proxy)))
+ src/Database/PostgreSQL/Store/Generics.hs view
@@ -0,0 +1,306 @@+{-# LANGUAGE ConstraintKinds,+             DataKinds,+             FlexibleContexts,+             FlexibleInstances,+             GADTs,+             StandaloneDeriving,+             TypeFamilies,+             TypeOperators,+             TypeSynonymInstances,+             UndecidableInstances+#-}++-- |+-- Module:     Database.PostgreSQL.Store.Generics+-- Copyright:  (c) Ole Krüger 2016+-- License:    BSD3+-- Maintainer: Ole Krüger <ole@vprsm.de>+module Database.PostgreSQL.Store.Generics (+	-- * Generic Entity+	EntityDataType,+	GenericEntity,++	toGenericEntity,+	fromGenericEntity,+++	-- * Type-Level Information+	KRecord (..),+	KFlatSum (..),+	KDataType (..),++	-- * Mapper classes+	GRecord (..),+	GFlatSum (..),+	GDataType (..),++	Record (..),+	FlatSum (..),+	DataType (..),++	-- * Analyzers+	AnalyzeRecordRep,+	AnalyzeFlatSumRep,+	AnalyzeDataType,++	AnalyzeEntity+) where++import GHC.Generics+import GHC.TypeLits++import Data.Kind++-- | Information about a record+data KRecord+	= TCombine KRecord KRecord+		-- ^ Combination of two records+	| TSingle Meta Type+		-- ^ Single element with meta information and type++-- | Mappings between a 'Generic' representation and our 'KRecord'-based representation+class GRecord (rec :: KRecord) where+	-- | 'Generic' representation+	type RecordRep rec :: * -> *++	-- | 'KRecord'-based representation+	data Record rec++	-- | From 'Generic' representation+	toRecord :: RecordRep rec x -> Record rec++	-- | To 'Generic' representation+	fromRecord :: Record rec -> RecordRep rec x++-- | Single record+instance GRecord ('TSingle meta typ) where+	type RecordRep ('TSingle meta typ) = S1 meta (Rec0 typ)++	data Record ('TSingle meta typ) = Single typ++	toRecord (M1 (K1 x)) = Single x++	fromRecord (Single x) = M1 (K1 x)++deriving instance (Show typ) => Show (Record ('TSingle meta typ))++-- | Combination of records+instance (GRecord lhs, GRecord rhs) => GRecord ('TCombine lhs rhs) where+	type RecordRep ('TCombine lhs rhs) = RecordRep lhs :*: RecordRep rhs++	data Record ('TCombine lhs rhs) = Combine (Record lhs) (Record rhs)++	toRecord (lhs :*: rhs) = Combine (toRecord lhs) (toRecord rhs)++	fromRecord (Combine lhs rhs) = fromRecord lhs :*: fromRecord rhs++deriving instance (Show (Record lhs), Show (Record rhs)) => Show (Record ('TCombine lhs rhs))++-- | Analyze the 'Generic' representation of the selectors. Make sure it has 1 or more fields. Then+-- transform it into a 'KRecord'.+type family AnalyzeRecordRep org (sel :: * -> *) :: KRecord where+	-- Single field+	AnalyzeRecordRep org (S1 meta (Rec0 typ)) =+		'TSingle meta typ++	-- Multiple fields+	AnalyzeRecordRep org (lhs :*: rhs) =+		'TCombine (AnalyzeRecordRep org lhs) (AnalyzeRecordRep org rhs)++	-- Missing field(s)+	AnalyzeRecordRep org U1 =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " has one constructor, therefore that constructor must have \+		                       \at least one field")++	-- Something else+	AnalyzeRecordRep org other =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " has a constructor with an invalid selector"+		           ':$$: 'ShowType other)++-- | Information about the constructors of an enumeration+data KFlatSum+	= TChoose KFlatSum KFlatSum+		-- ^ Combination of values+	| TValue Meta+		-- ^ Single value of the enumeration++-- | Mappings between a 'Generic' representation and our 'KFlatSum'-based representation+class GFlatSum (enum :: KFlatSum) where+	-- | 'Generic' representation+	type FlatSumRep enum :: * -> *++	-- | 'KFlatSum'-based representation+	data FlatSum enum++	-- | From 'Generic' representation+	toFlatSum :: FlatSumRep enum x -> FlatSum enum++	-- | To 'Generic' representation+	fromFlatSum :: FlatSum enum -> FlatSumRep enum x++-- | Single constructor+instance GFlatSum ('TValue meta) where+	type FlatSumRep ('TValue meta) = C1 meta U1++	data FlatSum ('TValue meta) = Unit++	toFlatSum (M1 U1) = Unit++	fromFlatSum Unit = M1 U1++deriving instance Show (FlatSum ('TValue meta))++-- | Combination of multiple constructors+instance (GFlatSum lhs, GFlatSum rhs) => GFlatSum ('TChoose lhs rhs) where+	type FlatSumRep ('TChoose lhs rhs) = FlatSumRep lhs :+: FlatSumRep rhs++	data FlatSum ('TChoose lhs rhs) = ChooseLeft (FlatSum lhs) | ChooseRight (FlatSum rhs)++	toFlatSum (L1 lhs) = ChooseLeft (toFlatSum lhs)+	toFlatSum (R1 rhs) = ChooseRight (toFlatSum rhs)++	fromFlatSum (ChooseLeft lhs)  = L1 (fromFlatSum lhs)+	fromFlatSum (ChooseRight rhs) = R1 (fromFlatSum rhs)++deriving instance (Show (FlatSum lhs), Show (FlatSum rhs)) => Show (FlatSum ('TChoose lhs rhs))++-- | Analyze the 'Generic' representation of constructors. Make sure every constructor has zero+-- fields. Then transform it into a 'KFlatSum'.+type family AnalyzeFlatSumRep org (cons :: * -> *) :: KFlatSum where+	-- Constructor without record selector+	AnalyzeFlatSumRep org (C1 meta U1) =+		'TValue meta++	-- Constructor with a record selector is invalid+	AnalyzeFlatSumRep org (C1 meta1 (S1 meta2 rec)) =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " has multiple constructors, therefore these constructors must have \+		                       \no fields")++	-- Constructor with a record selector is invalid+	AnalyzeFlatSumRep org (C1 meta1 (lhs :*: rhs)) =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " has multiple constructors, therefore these constructors must have \+		                       \no fields")++	-- More constructors+	AnalyzeFlatSumRep org (lhs :+: rhs) =+		'TChoose (AnalyzeFlatSumRep org lhs) (AnalyzeFlatSumRep org rhs)++	-- Something else+	AnalyzeFlatSumRep org other =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " has an invalid constructor"+		           ':$$: 'ShowType other)++-- | Information about a data type+data KDataType+	= TRecord Meta Meta KRecord+		-- ^ Record+	| TFlatSum Meta KFlatSum+		-- ^ Enumeration++-- | Mappings between a 'Generic' representation and our 'KDataType'-based representation+class GDataType (dat :: KDataType) where+	-- | 'Generic' representation+	type DataTypeRep dat :: * -> *++	-- | 'KDataType'-based representation+	data DataType dat++	-- | From 'Generic' representation+	toDataType :: DataTypeRep dat x -> DataType dat++	-- | To 'Generic' representation+	fromDataType :: DataType dat -> DataTypeRep dat x++-- | With single constructor+instance (GRecord rec) => GDataType ('TRecord d c rec) where+	type DataTypeRep ('TRecord d c rec) = D1 d (C1 c (RecordRep rec))++	data DataType ('TRecord d c rec) = Record (Record rec)++	toDataType (M1 (M1 rec)) = Record (toRecord rec)++	fromDataType (Record rec) = M1 (M1 (fromRecord rec))++deriving instance (Show (Record rec)) => Show (DataType ('TRecord d c rec))++-- | With multiple constructors+instance (GFlatSum enum) => GDataType ('TFlatSum d enum) where+	type DataTypeRep ('TFlatSum d enum) = D1 d (FlatSumRep enum)++	data DataType ('TFlatSum d enum) = FlatSum (FlatSum enum)++	toDataType (M1 enum) = FlatSum (toFlatSum enum)++	fromDataType (FlatSum flatSum) = M1 (fromFlatSum flatSum)++deriving instance (Show (FlatSum enum)) => Show (DataType ('TFlatSum d enum))++-- | Analyze the 'Generic' representation of a data type. If only one constructor exists, further+-- analyzing is delegated to 'AnalyzeRecordRep'. When two or more exist, analyzing is performed by+-- 'AnalyzeFlatSumRep'. The results are gather in a 'KDataType' instance.+type family AnalyzeDataType org (dat :: * -> *) :: KDataType where+	-- Single constructor+	AnalyzeDataType org (D1 meta1 (C1 meta2 sel)) =+		'TRecord meta1 meta2 (AnalyzeRecordRep org sel)++	-- Multiple constructors+	AnalyzeDataType org (D1 meta (lhs :+: rhs)) =+		'TFlatSum meta (AnalyzeFlatSumRep org (lhs :+: rhs))++	-- Missing constructor(s)+	AnalyzeDataType org (D1 meta V1) =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " must have a constructor")++	-- Data type with constructor(s) that does not match the given patterns+	AnalyzeDataType org (D1 meta other) =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " has an invalid constructor"+		           ':$$: 'ShowType other)++	-- Something else+	AnalyzeDataType org other =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " is not a valid data type"+		           ':$$: 'ShowType other)++-- | Analyze the 'Generic' representation of a type, in order to generate its 'KDataType' instance.+type AnalyzeEntity a = AnalyzeDataType a (Rep a)++-- | Analyze the 'Generic' representation of a type to figure out which 'DataType' it needs.+type EntityDataType a = DataType (AnalyzeEntity a)++-- | Make sure @a@ has a safe generic representation. Types that qualify implement 'Generic' and+-- fulfill one of the following criteria:+--+--  * single constructor with 1 or more fields+--  * multiple constructors with no fields+--+-- This constraint is mostly utilized to give the user more information about why their type has+-- been rejected.+type GenericEntity a = (Generic a,+                        GDataType (AnalyzeEntity a),+                        DataTypeRep (AnalyzeEntity a) ~ Rep a)++-- | Convert to entity representation.+fromGenericEntity :: (GenericEntity a) => a -> EntityDataType a+fromGenericEntity =+	toDataType . from++-- | Build from entity representation.+toGenericEntity :: (GenericEntity a) => EntityDataType a -> a+toGenericEntity =+	to . fromDataType
− src/Database/PostgreSQL/Store/OIDs.hs
@@ -1,91 +0,0 @@-{-# LANGUAGE TemplateHaskell, QuasiQuotes, RankNTypes #-}---- |--- Module:     Database.PostgreSQL.Store.OIDs--- Copyright:  (c) Ole Krüger 2016--- License:    BSD3--- Maintainer: Ole Krüger <ole@vprsm.de>-module Database.PostgreSQL.Store.OIDs (-	OIDQ,--	bool,-	int2,-	int4,-	int8,-	float4,-	float8,-	numeric,-	char,-	varchar,-	text,-	bytea,-	timestamp,-	timestamptz-) where--import           Language.Haskell.TH-import qualified Database.PostgreSQL.LibPQ as P--class GenOID a where-	genOID :: Integer -> Q a--instance GenOID Exp where-	genOID oid = [e| P.Oid $(litE (IntegerL oid)) |]--instance GenOID Pat where-	genOID oid = [p| P.Oid $(pure (LitP (IntegerL oid))) |]---- | A type which can be coerced into @Q Exp@ or @Q Pat@.-type OIDQ = forall a . GenOID a => Q a---- | Boolean-bool :: OIDQ-bool = genOID 16---- | 16-bit integer-int2 :: OIDQ-int2 = genOID 21---- | 32-bit integer-int4 :: OIDQ-int4 = genOID 23---- | 64-bit integer-int8 :: OIDQ-int8 = genOID 20---- | Single-precision floating-point number-float4 :: OIDQ-float4 = genOID 700---- | Double-precision floating-point number-float8 :: OIDQ-float8 = genOID 701---- | Arbitrary precision number-numeric :: OIDQ-numeric = genOID 1700---- | Fixed-length string-char :: OIDQ-char    = genOID 1042---- | Variable-length string-varchar :: OIDQ-varchar = genOID 1043---- | Unlimited variable-length string-text :: OIDQ-text = genOID 25---- | Byte array-bytea :: OIDQ-bytea = genOID 17---- | Timestamp without timezone-timestamp :: OIDQ-timestamp = genOID 1114---- | Timestamp with timezone-timestamptz :: OIDQ-timestamptz = genOID 1184
src/Database/PostgreSQL/Store/Query.hs view
@@ -1,444 +1,24 @@-{-# LANGUAGE QuasiQuotes, TemplateHaskell, BangPatterns,-             GeneralizedNewtypeDeriving, MultiParamTypeClasses, FunctionalDependencies,-             FlexibleInstances, FlexibleContexts, TypeFamilies #-}- -- | -- Module:     Database.PostgreSQL.Store.Query--- Copyright:  (c) Ole Krüger 2015-2016+-- Copyright:  (c) Ole Krüger 2016 -- License:    BSD3 -- Maintainer: Ole Krüger <ole@vprsm.de>+--+-- This module acts solely as a re-export unit. module Database.PostgreSQL.Store.Query (-	-- * Query-	Query (..),-	SelectorElement (..),-	QueryTable (..),-	pgsq,-	pgss,--	-- * Helpers-	quoteIdentifier,+	-- * Exported modules+	module Database.PostgreSQL.Store.Query.Builder,+	module Database.PostgreSQL.Store.Query.TH, -	-- * Query builder-	QueryCode,-	QueryBuildable,-	QueryBuilder,-	runQueryBuilder,-	writeCode,-	writeStringCode,-	writeIdentifier,-	writeAbsIdentifier,-	writeParam,-	writeColumn,-	intercalateBuilder+	-- * Utilities+	castQuery ) where -import           Language.Haskell.TH-import           Language.Haskell.TH.Quote--import           Control.Applicative-import           Control.Monad.State.Strict--import           Data.List-import           Data.Proxy-import           Data.Monoid-import           Data.String-import qualified Data.Text       as T-import qualified Data.ByteString as B--import           Data.Char-import           Data.Attoparsec.Text--import           Database.PostgreSQL.Store.Columns---- | Properly quote an identifier.-quoteIdentifier :: String -> String-quoteIdentifier name =-	'"' : stripQuotes name ++ "\""-	where-		stripQuotes []         = []-		stripQuotes ('"' : xs) = '"' : '"' : stripQuotes xs-		stripQuotes (x : xs)   = x : stripQuotes xs---- | Query including statement and parameters------ Use the 'pgsq' quasi-quoter to conveniently create queries.-data Query = Query {-	-- | Statement-	queryStatement :: !B.ByteString,--	-- | Parameters-	queryParams :: ![Value]-} deriving (Show, Eq, Ord)---- | @SELECT@ expression-data SelectorElement-	= SelectorField String-	  -- ^ Select a field. The field nme will be quoted and properly escaped.-	| SelectorSpecial String-	  -- ^ Select a special expression. The expression will be inlined as is.-	deriving (Show, Eq, Ord)---- | A type which implements this class can be used as a table in a quasi-quoted query.---   'mkTable' can implement this for you.-class QueryTable a where-	-- | Unquoted name of the table-	tableName :: Proxy a -> String--	-- | Unquoted name of the ID field-	tableIDName :: Proxy a -> String--	-- | Selectors needed to retrieve all fields necessary to construct the type - think @SELECT@.-	tableSelectors :: Proxy a -> [SelectorElement]---- | Generate the quoted identifier for a table.-makeTableIdentifier :: (QueryTable a, IsString b) => Proxy a -> b-makeTableIdentifier proxy =-	fromString (quoteIdentifier (tableName proxy))---- | Generate the quoted identifier for table's id column.-makeTableIDIdentifier :: (QueryTable a, IsString b) => Proxy a -> b-makeTableIDIdentifier proxy =-	fromString (quoteIdentifier (tableName proxy) ++-	            "." ++-	            quoteIdentifier (tableIDName proxy))---- | Generate the list of expression used as selector.-makeTableSelectors :: (QueryTable a, IsString b) => Proxy a -> b-makeTableSelectors proxy =-	fromString (intercalate ", " (map makeElement (tableSelectors proxy)))-	where-		makeElement (SelectorField name) =-			quoteIdentifier (tableName proxy) ++ "." ++ quoteIdentifier name-		makeElement (SelectorSpecial expr) =-			expr---- | Query segment-data Segment-	= SSelector String-	| STable String-	| SVariable String-	| SIdentifier String-	| SQuote Char String-	| SOther String---- | Name-name :: Parser String-name =-	(:) <$> (letter <|> char '_') <*> many (satisfy isAlphaNum <|> char '_')---- | Type name-typeName :: Parser String-typeName =-	(:) <$> satisfy isUpper <*> many (satisfy isAlphaNum <|> char '_')---- | Qualified type name-qualifiedTypeName :: Parser String-qualifiedTypeName = do-	intercalate "." <$> sepBy1 typeName (char '.')---- | Quote-quote :: Char -> Parser Segment-quote delim = do-	char delim-	cnt <- concat <$> many (choice [escapedDelim, notDelim])-	char delim-	pure (SQuote delim cnt)-	where-		escapedDelim = (\ a b -> [a, b]) <$> char '\\' <*> char delim-		notDelim = (: []) <$> notChar delim---- | Segments-segments :: Parser [Segment]-segments =-	many (choice [quote '"',-	              quote '\'',-	              char '#' >> SSelector <$> qualifiedTypeName,-	              char '@' >> STable <$> qualifiedTypeName,-	              char '&' >> SIdentifier <$> qualifiedTypeName,-	              char '$' >> SVariable <$> name,-	              SOther "#" <$ char '#',-	              SOther "@" <$ char '@',-	              SOther "&" <$ char '&',-	              SOther "$" <$ char '$',-	              SOther <$> some (satisfy (notInClass "\"'#@&$"))])---- | Reduce segments in order to resolve names and collect query parameters.-reduceSegment :: Segment -> QueryBuilder [Q Exp] (Q Exp)-reduceSegment seg =-	case seg of-		SOther str ->-			writeStringCode str--		SQuote delim cnt ->-			writeStringCode (delim : cnt ++ [delim])--		SVariable varName -> writeParam $ do-			mbName <- lookupValueName varName-			case mbName of-				Just name -> [e| pack $(varE name) |]-				Nothing   -> fail ("'" ++ varName ++ "' does not refer to anything")--		STable tableName -> writeCode $ do-			mbName <- lookupTypeName tableName-			case mbName of-				Just table -> [e| makeTableIdentifier (Proxy :: Proxy $(conT table)) |]-				Nothing    -> fail ("'" ++ tableName ++ "' does not refer to anything")--		SSelector tableName -> writeCode $ do-			mbName <- lookupTypeName tableName-			case mbName of-				Just table -> [e| makeTableSelectors (Proxy :: Proxy $(conT table)) |]-				Nothing    -> fail ("'" ++ tableName ++ "' does not refer to anything")--		SIdentifier tableName -> writeCode $ do-			mbName <- lookupTypeName tableName-			case mbName of-				Just table -> [e| makeTableIDIdentifier (Proxy :: Proxy $(conT table)) |]-				Nothing    -> fail ("'" ++ tableName ++ "' does not refer to anything")---- | Parse quasi-quoted query.-parseStoreQueryE :: String -> Q Exp-parseStoreQueryE code = do-	case parseOnly (segments <* endOfInput) (T.pack code) of-		Left msg ->-			fail msg--		Right xs -> do-			runQueryBuilder (mapM_ reduceSegment xs)---- | This quasi-quoter allows you to generate instances of 'Query'. It lets you write SQL with some--- small enhancements. 'pgsq' heavily relies on 'QueryTable' which can be implemented by 'mkTable'--- for a type of your choice.------ Some syntax definitions that might be useful later on:------ > TypeName          ::= UpperAlpha {AlphaNumeric | '_'}--- > Name              ::= (Alpha | '_') {AlphaNumeric | '_'}--- > QualifiedTypeName ::= {TypeName '.'} TypeName------ @Alpha@ includes all alphabetical characters; @UpperAlpha@ includes all upper-case alphabetical--- characters; @AlphaNumeric@ includes all alpha-numeric characters.------ = Embed values--- You can embed values whose types implement 'Column'.------ > ValueExp ::= '$' Name------ > magicNumber :: Int--- > magicNumber = 1337--- >--- > myQuery :: Query--- > myQuery =--- >     [pgsq| SELECT * FROM table t WHERE t.column1 > $magicNumber AND t.column2 < $otherNumber |]--- >     where otherNumber = magicNumber * 2------ @$magicNumber@ and @$otherNumber@ are references to values @magicNumber@ and @otherNumber@.------ The quasi-quoter will generate a 'Query' expression similar to the following.------ > Query "SELECT * FROM table t WHERE t.column1 > $1 AND t.column2 < $2"--- >       [pack magicNumber, pack otherNumber]------ = Table names--- Types that implement 'QueryTable' associate a table name with themselves. Since the table name is--- not always known to the user, one can insert it dynamically.------ > TableNameExp ::= '@' QualifiedTypeName------ The @\@@-operators is also an alias for the function @ABS@. If you have an expression that--- triggers the quasi-quoter such as @\@A@, but you would like to use the @ABS@ functionality, then--- simply reformat your expression to @\@(A)@ or @ABS(A)@.------ > instance QueryTable YourType where--- >     tableName _ = "YourTable"--- >--- > myQuery :: Query--- > myQuery =--- >     [pgsq| SELECT * FROM @YourType WHERE @YourType.column = 1337 |]------ The table name will be inlined which results in the following.------ > Query "SELECT * FROM \"YourTable\" WHERE \"YourTable\".column = 1337" []------ = Identifier column names--- Each instance of 'QueryTable' also provides the name of the identifier column. Using this column--- name you can identify specific rows of a certain table.------ > TableIdentExp ::= '&' TypeName------ @&@ is also the operator for bitwise-AND. To resolve the ambiguity for expressions like @A&B@,--- simply reformat it to @A & B@ or @A&(B)@.------ > instance QueryTable YourType where--- >     tableName _   = "YourTable"--- >     tableIDName _ = "id"--- >--- > listIDs :: Query--- > listIDs =--- >     [pgsq| SELECT &YourType FROM @YourType |]------ @listIDs@ is now a query which lists the IDs of each row. This is especially useful in--- combination with 'Reference'.------ > fetchIDs :: Errand [Reference YourType]--- > fetchIDs =--- >     query [pgsq| SELECT &YourType FROM @YourType |]------ = Selectors--- 'mkTable' will automatically implement 'Result' and 'QueryTable' for you. This allows you to make--- use of the selector expander.------ > SelectorExp ::= '#' QualifiedTypeName------ @#@ is also the operator for bitwise-XOR. To resolve the ambiguity for expressions like @A#B@,--- simply reformat it to @A # B@ or @A#(B)@ or @A#\"B\"@.------ > data Actor = Actor {--- >     actorName :: String,--- >     actorAge  :: Word--- > } deriving (Show, Eq, Ord)--- >--- > mkTable ''Actor []--- >--- > fetchOldActors :: Errand [Actor]--- > fetchOldActors =--- >     query [pgsq| SELECT #Actor FROM @Actor a WHERE a.actorAge >= $oldAge |]--- >     where oldAge = 70------ @#Actor@ will expand to a list of columns that are necessary to construct an instance of @Actor@.--- In this case it is equivalent to------ > @Actor.actorName, @Actor.actorAge-pgsq :: QuasiQuoter-pgsq =-	QuasiQuoter {-		quoteExp  = parseStoreQueryE,-		quotePat  = const (fail "Cannot use 'pgsq' in pattern"),-		quoteType = const (fail "Cannot use 'pgsq' in type"),-		quoteDec  = const (fail "Cannot use 'pgsq' in declaration")-	}---- | Parse quasi-quoted query but return only statement.-parseStoreStatementE :: String -> Q Exp-parseStoreStatementE code = do-	case parseOnly (segments <* endOfInput) (T.pack code) of-		Left msg ->-			fail (show msg)--		Right xs -> do-			[e| mconcat $(listE (runQueryBuilder_ (mapM_ reduceSegment xs))) |]---- | Just like 'pgsq' but only produces the statement associated with the query. Referenced--- values are not inlined, they are simply dismissed.-pgss :: QuasiQuoter-pgss =-	QuasiQuoter {-		quoteExp  = parseStoreStatementE,-		quotePat  = const (fail "Cannot use 'pgss' in pattern"),-		quoteType = const (fail "Cannot use 'pgss' in type"),-		quoteDec  = const (fail "Cannot use 'pgss' in declaration")-	}---- | Internal state for every builder-data BuilderState s p = BuilderState s Word [p]---- |-class QueryCode s where-	type Code s--	appendCode :: s -> Code s -> s--	appendStringCode :: s -> String -> s--instance QueryCode B.ByteString where-	type Code B.ByteString = B.ByteString--	appendCode = B.append--	appendStringCode bs str = bs <> fromString str--instance QueryCode [Q Exp] where-	type Code [Q Exp] = Q Exp--	appendCode segments exp = segments ++ [exp]--	appendStringCode segments str = appendCode segments [e| fromString $(stringE str) |]--instance QueryCode String where-	type Code String = String--	appendCode segments code = segments ++ code--	appendStringCode = appendCode---- | Can build @o@ using @s@ and @[p]@.-class QueryBuildable s p o | s p -> o where-	buildQuery :: s -> [p] -> o--instance QueryBuildable B.ByteString Value Query where-	buildQuery = Query--instance QueryBuildable String (Q Exp) (Q Exp) where-	buildQuery code params =-		[e| Query (fromString $(stringE code)) $(listE params) |]--instance QueryBuildable [Q Exp] (Q Exp) (Q Exp) where-	buildQuery codeSegments params =-		[e| Query (B.concat $(listE codeSegments)) $(listE params) |]---- | Query builder-type QueryBuilder s p = State (BuilderState s p) ()---- | Run query builder.-runQueryBuilder :: (QueryBuildable s p o, Monoid s) => QueryBuilder s p -> o-runQueryBuilder builder =-	buildQuery code params-	where-		BuilderState code _ params = execState builder (BuilderState mempty 1 [])---- | Run query builder, return only the statement.-runQueryBuilder_ :: (Monoid s) => QueryBuilder s p -> s-runQueryBuilder_ builder =-	code-	where-		BuilderState code _ _ = execState builder (BuilderState mempty 1 [])---- | Write code.-writeCode :: (QueryCode s) => Code s -> QueryBuilder s p-writeCode code =-	modify $ \ (BuilderState stmt idx params) ->-		BuilderState (appendCode stmt code) idx params---- | Write string code.-writeStringCode :: (QueryCode s) => String -> QueryBuilder s p-writeStringCode code =-	modify $ \ (BuilderState stmt idx params) ->-		BuilderState (appendStringCode stmt code) idx params---- | Add an identifier.-writeIdentifier :: (QueryCode s) => String -> QueryBuilder s p-writeIdentifier name =-	writeStringCode (quoteIdentifier name)---- | Add an absolute identifier.-writeAbsIdentifier :: (QueryCode s) => String -> String -> QueryBuilder s p-writeAbsIdentifier ns name = do-	writeIdentifier ns-	writeStringCode "."-	writeIdentifier name---- | Embed a parameter.-writeParam :: (QueryCode s) => p -> QueryBuilder s p-writeParam param = do-	modify $ \ (BuilderState code idx params) ->-		BuilderState (appendStringCode code ("$" ++ show idx)) (idx + 1) (params ++ [param])---- | Embed a value parameter.-writeColumn :: (Column p, QueryCode s) => p -> QueryBuilder s Value-writeColumn param =-	writeParam (pack param)+import Database.PostgreSQL.Store.Types+import Database.PostgreSQL.Store.Query.Builder+import Database.PostgreSQL.Store.Query.TH --- | Do something between other builders.-intercalateBuilder :: QueryBuilder s p -> [QueryBuilder s p] -> QueryBuilder s p-intercalateBuilder x xs =-	sequence_ (intersperse x xs)+-- | Cast the query's result type.+castQuery :: Query a -> Query b+castQuery (Query s p) =+	Query s p
+ src/Database/PostgreSQL/Store/Query/Builder.hs view
@@ -0,0 +1,131 @@+{-# LANGUAGE OverloadedStrings, RecordWildCards, TypeSynonymInstances, FlexibleInstances #-}++-- |+-- Module:     Database.PostgreSQL.Store.Query.Builder+-- Copyright:  (c) Ole Krüger 2016+-- License:    BSD3+-- Maintainer: Ole Krüger <ole@vprsm.de>+module Database.PostgreSQL.Store.Query.Builder (+	-- * Query Builder+	QueryBuilder,+	insertCode,+	insertTypedValue,+	insertValue,+	insertValue',+	insertQuote,+	insertName,++	insertCommaSeperated,++	-- * Generalized Building+	FromQueryBuilder (..)+) where++import           Control.Monad.State.Strict++import           Data.List+import qualified Data.ByteString           as B++import           Database.PostgreSQL.LibPQ (invalidOid)++import           Database.PostgreSQL.Store.Types+import           Database.PostgreSQL.Store.Utilities++-- | Internal builder state+data BuilderState = BuilderState {+	queryCode   :: B.ByteString,+	queryIndex  :: Word,+	queryValues :: [TypedValue]+}++-- | Query builder+type QueryBuilder = State BuilderState ()++-- | Insert a piece of SQL.+insertCode :: B.ByteString -> QueryBuilder+insertCode code =+	modify (\ state -> state {queryCode = B.append (queryCode state) code})++-- | Insert a parameter placeholder into the code and attach the typed value to the query.+insertTypedValue :: TypedValue -> QueryBuilder+insertTypedValue typedValue =+	modify $ \ BuilderState {..} ->+		BuilderState {+			queryCode = B.concat [queryCode, B.singleton 36, showByteString queryIndex],+			queryIndex = queryIndex + 1,+			queryValues = queryValues ++ [typedValue]+		}++-- | Same as 'insertTypedValue' but untyped.+insertValue :: Value -> QueryBuilder+insertValue value =+	insertTypedValue (TypedValue invalidOid (Just value))++-- | Extension of 'insertValue' which will add a type hint to the parameter placeholder.+insertValue' :: B.ByteString -> Value -> QueryBuilder+insertValue' typ value = do+	insertCode "("+	insertValue value+	insertCode "::"+	insertCode typ+	insertCode ")"++-- | Insert a quote into the code.+insertQuote :: B.ByteString -> QueryBuilder+insertQuote contents =+	insertCode (B.concat [B.singleton 39, -- '+	                      B.concatMap replaceDelim contents,+	                      B.singleton 39])+	where+		replaceDelim 39 = B.pack [39, 39]+		replaceDelim x  = B.singleton x++-- | Join several builders into a comma-seperated list.+insertCommaSeperated :: [QueryBuilder] -> QueryBuilder+insertCommaSeperated bs =+	sequence_ (intersperse (insertCode ",") bs)++-- | Insert a name into the code. It will be surrounded by double quotes if necessary.+insertName :: B.ByteString -> QueryBuilder+insertName name =+	if isAllowed then+		insertCode name+	else+		insertCode (B.concat [B.singleton 34, -- "+		                      B.intercalate (B.pack [34, 34]) (B.split 34 name),+		                      B.singleton 34])+	where+		isAllowedHead b =+			(b >= 97 && b <= 122)    -- 'a' to 'z'+			|| (b >= 65 && b <= 90)  -- 'A' to 'Z'+			|| b == 95               -- '_'++		isAllowedBody b =+			isAllowedHead b+			|| (b >= 48 && b <= 57)  -- '0' to '9'++		isAllowed =+			case B.uncons name of+				Nothing -> True+				Just (h, b) -> isAllowedHead h && B.all isAllowedBody b++-- | @a@ can be instantiated using the query builder.+class FromQueryBuilder a where+	buildQuery :: QueryBuilder -> a++instance FromQueryBuilder QueryBuilder where+	buildQuery = id++instance FromQueryBuilder B.ByteString where+	buildQuery builder =+		queryCode (execState builder (BuilderState B.empty 1 []))++instance FromQueryBuilder (B.ByteString, [TypedValue]) where+	buildQuery builder =+		(code, values)+		where BuilderState code _ values = execState builder (BuilderState B.empty 1 [])++instance FromQueryBuilder (Query a) where+	buildQuery builder =+		Query code values+		where BuilderState code _ values = execState builder (BuilderState B.empty 1 [])
+ src/Database/PostgreSQL/Store/Query/TH.hs view
@@ -0,0 +1,230 @@+{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}++-- |+-- Module:     Database.PostgreSQL.Store.Query.TH+-- Copyright:  (c) Ole Krüger 2016+-- License:    BSD3+-- Maintainer: Ole Krüger <ole@vprsm.de>+module Database.PostgreSQL.Store.Query.TH (+	-- * Template Haskell+	parseQuery,+	pgsq+) where++import           Language.Haskell.TH+import           Language.Haskell.TH.Quote+import           Language.Haskell.Meta.Parse++import           Control.Applicative++import           Data.List+import           Data.Proxy+import           Data.Char+import           Data.Attoparsec.Text+import qualified Data.ByteString                    as B+import qualified Blaze.ByteString.Builder           as B+import qualified Blaze.ByteString.Builder.Char.Utf8 as B+import qualified Data.Text                          as T++import           Database.PostgreSQL.Store.Utilities+import           Database.PostgreSQL.Store.Entity+import           Database.PostgreSQL.Store.Table+import           Database.PostgreSQL.Store.Query.Builder++-- | Name+valueName :: Parser String+valueName =+	(:) <$> (letter <|> char '_') <*> many (satisfy isAlphaNum <|> char '_' <|> char '\'')++-- | Type name+typeName :: Parser String+typeName =+	(:) <$> satisfy isUpper <*> many (satisfy isAlphaNum <|> char '_' <|> char '\'')++-- | Qualified type name+qualifiedTypeName :: Parser String+qualifiedTypeName =+	intercalate "." <$> sepBy1 typeName (char '.')++-- | Query segment+data QuerySegment+	= QueryEntity String+	| QueryEntityCode String+	| QueryQuote Char String+	| QueryOther String+	| QueryTable String+	| QuerySelector String+	| QuerySelectorAlias String String+	-- QueryIdentifier String+	deriving (Show, Eq, Ord)++-- | Table+tableSegment :: Parser QuerySegment+tableSegment = do+	char '@'+	QueryTable <$> qualifiedTypeName++-- | Selector+selectorSegment :: Parser QuerySegment+selectorSegment = do+	char '#'+	QuerySelector <$> qualifiedTypeName++-- | Selector alias+selectorAliasSegment :: Parser QuerySegment+selectorAliasSegment = do+	char '#'+	QuerySelectorAlias <$> qualifiedTypeName+	                   <*  char '('+	                   <*> valueName+	                   <*  char ')'++-- -- | Identifier+-- identifierSegment :: Parser QuerySegment+-- identifierSegment = do+-- 	char '&'+-- 	QueryIdentifier <$> qualifiedTypeName++-- | Entity+entityNameSegment :: Parser QuerySegment+entityNameSegment = do+	char '$'+	QueryEntity <$> valueName++-- | Entity code+entityCodeSegment :: Parser QuerySegment+entityCodeSegment =+	QueryEntityCode <$> (string "$(" *> insideCode <* char ')')+	where+		insideCode =+			concat <$> many (choice [bracedCode,+			                         quoteCode '\'',+			                         quoteCode '\"',+			                         some (satisfy (notInClass "\"'()"))])++		bracedCode =+			char '(' *> fmap (\ code -> '(' : code ++ ")") insideCode <* char ')'++		quoteCode delim = do+			char delim+			cnt <- many (choice [escapedDelim delim, notDelim delim])+			char delim+			pure (delim : concat cnt ++ [delim])++		escapedDelim delim = do+			char '\\'+			char delim+			pure ['\\', delim]++		notDelim delim =+			(: []) <$> notChar delim++-- | Quotation+quoteSegment :: Char -> Parser QuerySegment+quoteSegment delim = do+	char delim+	cnt <- concat <$> many (choice [escapedDelim, notDelim])+	char delim+	pure (QueryQuote delim cnt)+	where+		escapedDelim = char delim >> char delim >> pure [delim, delim]+		notDelim = (: []) <$> notChar delim++-- | Uninterpreted segment+otherSegment :: Parser QuerySegment+otherSegment =+	QueryOther <$> some (satisfy (notInClass "\"'@#$"))+	-- QueryOther <$> some (satisfy (notInClass "\"'@&#$"))++-- | Segment that is part of the query+querySegment :: Parser QuerySegment+querySegment =+	choice [quoteSegment '\'',+	        quoteSegment '"',+	        tableSegment,+	        selectorAliasSegment,+	        selectorSegment,+	        -- identifierSegment,+	        entityCodeSegment,+	        entityNameSegment,+	        otherSegment]++-- | Pack 'String' code into a 'ByteString'.+packCode :: String -> B.ByteString+packCode code =+	B.toByteString (B.fromString code)++-- | Translate a "QuerySegment" to an expression.+translateSegment :: QuerySegment -> Q Exp+translateSegment segment =+	case segment of+		QueryTable stringName -> do+			mbTypeName <- lookupTypeName stringName+			case mbTypeName of+				Nothing -> fail ("'" ++ stringName ++ "' does not refer to a type")+				Just typ ->+					[e| insertName (tableName (describeTableType (Proxy :: Proxy $(conT typ)))) |]++		QuerySelector stringName -> do+			mbTypeName <- lookupTypeName stringName+			case mbTypeName of+				Nothing -> fail ("'" ++ stringName ++ "' does not refer to a type")+				Just typ ->+					[e| insertColumns (describeTableType (Proxy :: Proxy $(conT typ))) |]++		QuerySelectorAlias stringName aliasName -> do+			mbTypeName <- lookupTypeName stringName+			case mbTypeName of+				Nothing -> fail ("'" ++ stringName ++ "' does not refer to a type")+				Just typ ->+					[e| insertColumnsOn (describeTableType (Proxy :: Proxy $(conT typ)))+					                    $(liftByteString (buildByteString aliasName)) |]++		-- QueryIdentifier stringName -> do+		-- 	mbTypeName <- lookupTypeName stringName+		-- 	case mbTypeName of+		-- 		Nothing -> fail ("'" ++ stringName ++ "' does not refer to a type")+		-- 		Just typ ->+		-- 			[e| insertTableIdentColumnName (Proxy :: Proxy $(conT typ)) |]++		QueryEntity stringName -> do+			mbValueName <- lookupValueName stringName+			case mbValueName of+				Nothing -> fail ("'" ++ stringName ++ "' does not refer to a value")+				Just name ->+					[e| insertEntity $(varE name) |]++		QueryEntityCode code ->+			case parseExp code of+				Left msg -> fail ("Error in code " ++ show code ++ ": " ++ msg)+				Right expr ->+					[e| insertEntity $(pure expr) |]++		QueryQuote delim code ->+			[e| insertCode $(liftByteString (packCode (delim : code ++ [delim]))) |]++		QueryOther code ->+			[e| insertCode $(liftByteString (packCode code)) |]++-- | Parse a query string in order to produce a 'QueryBuilder' expression.+parseQuery :: String -> Q Exp+parseQuery code =+	case parseOnly (many querySegment <* endOfInput) (T.strip (T.pack code)) of+		Left msg ->+			fail ("Query parser failed: " ++ msg)++		Right [] ->+			[e| buildQuery (pure ()) |]++		Right segments ->+			[e| buildQuery $(DoE . map NoBindS <$> mapM translateSegment segments) |]++-- | Generate queries conveniently. See 'BuildQuery' to find out which types can be produced.+pgsq :: QuasiQuoter+pgsq =+	QuasiQuoter {+		quoteExp  = parseQuery,+		quotePat  = const (fail "Cannot use 'pgsq' in pattern"),+		quoteType = const (fail "Cannot use 'pgsq' in type"),+		quoteDec  = const (fail "Cannot use 'pgsq' in declaration")+	}
− src/Database/PostgreSQL/Store/Result.hs
@@ -1,82 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}---- |--- Module:     Database.PostgreSQL.Store.Result--- Copyright:  (c) Ole Krüger 2015-2016--- License:    BSD3--- Maintainer: Ole Krüger <ole@vprsm.de>-module Database.PostgreSQL.Store.Result (-	-- * Result processor-	ResultError (..),-	ResultProcessor,-	processResult,-	processOneResult,--	skipColumn,-	unpackColumn-) where--import           Control.Monad-import           Control.Monad.Trans-import           Control.Monad.Reader-import           Control.Monad.State.Strict-import           Control.Monad.Except--import qualified Database.PostgreSQL.LibPQ as P-import           Database.PostgreSQL.Store.Columns---- | Error that occured during result processing-data ResultError-	= TooFewColumnsError P.Column-	  -- ^ Occurs when you're trying to access a column that does not exist.-	| UnpackError P.Row P.Column P.Oid P.Format-	  -- ^ The value at a given row and column could not be unpacked.-	deriving (Show, Eq)---- | Result processor-newtype ResultProcessor a =-	ResultProcessor (StateT P.Column (ReaderT (P.Result, P.Row, P.Column) (ExceptT ResultError IO)) a)-	deriving (Functor, Applicative, Monad, MonadIO, MonadError ResultError)---- | Move cursor to the next column.-skipColumn :: ResultProcessor ()-skipColumn =-	ResultProcessor (modify (+ 1))---- | Unpack the current column and move the cursor to the next column.-unpackColumn :: (Column a) => ResultProcessor a-unpackColumn = do-	-- Gather information-	col <- ResultProcessor get-	(res, row, numCol) <- ResultProcessor ask--	-- Make sure we're not trying to unpack a non-existing column-	when (col >= numCol) (throwError (TooFewColumnsError numCol))--	-- Retrieve column-specific information-	(typ, mbData) <- liftIO $-		(,) <$> P.ftype res col <*> P.getvalue' res row col--	-- Try to unpack the value-	case unpack (maybe NullValue (Value typ) mbData) of-		Just ret -> ResultProcessor (put (col + 1) >> pure ret)-		Nothing  -> throwError (UnpackError row col typ P.Text)---- | Process the entire result set.-processResult :: P.Result -> ResultProcessor a -> ExceptT ResultError IO [a]-processResult res (ResultProcessor proc) = do-	(rows, cols) <- liftIO ((,) <$> P.ntuples res <*> P.nfields res)--	-- Iterate over each row number and run the row processor-	forM [0 .. rows - 1] $ \ row ->-		runReaderT (evalStateT proc 0) (res, row, cols)---- | Process one row of the result set.-processOneResult :: P.Result -> ResultProcessor a -> ExceptT ResultError IO (Maybe a)-processOneResult res (ResultProcessor proc) = do-	(rows, cols) <- liftIO ((,) <$> P.ntuples res <*> P.nfields res)--	if rows > 0 then-		Just <$> runReaderT (evalStateT proc 0) (res, 0, cols)-	else-		pure Nothing
+ src/Database/PostgreSQL/Store/RowParser.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++-- |+-- Module:     Database.PostgreSQL.Store.RowParser+-- Copyright:  (c) Ole Krüger 2016+-- License:    BSD3+-- Maintainer: Ole Krüger <ole@vprsm.de>+module Database.PostgreSQL.Store.RowParser (+	-- * Row Parser+	RowParser,+	RowErrorLocation (..),+	RowErrorDetail (..),+	RowError (..),++	rowNumber,+	columnNumber,+	columnsLeft,++	fetchColumn,+	peekColumn,+	parseColumn,++	peekContents,+	fetchContents,+	parseContents,++	skipColumn,++	-- * Result Parser+	parseResult+) where++import           Control.Monad+import           Control.Monad.Except+import           Control.Monad.Reader+import           Control.Monad.State.Strict++import qualified Data.ByteString           as B++import qualified Database.PostgreSQL.LibPQ as P+import           Database.PostgreSQL.Store.Types++-- | Location of an error+data RowErrorLocation = RowErrorLocation P.Column P.Row+	deriving (Show, Eq, Ord)++-- | Errors that occur during row parsing+data RowErrorDetail+	= TooFewColumns+		-- ^ Underlying 'RowParser' wants more columns than are currently present.+	| ColumnRejected TypedValue+		-- ^ A column value could not be parsed.+	| ContentsRejected (Maybe B.ByteString)+		-- ^ The contents of a column could not be parsed.+	deriving (Show, Eq, Ord)++-- | An error that occured when parsing a row.+data RowError = RowError RowErrorLocation RowErrorDetail+	deriving (Show, Eq, Ord)++-- | Static result information+data ResultInfo = ResultInfo P.Result -- Result to operate on+                             P.Column -- Total number of columns++-- | Static row information+data RowInput = RowInput ResultInfo -- Result info+                         P.Row      -- Current row to operate on++-- | Row parser+newtype RowParser a =+	RowParser (ReaderT RowInput (StateT P.Column (ExceptT RowError IO)) a)+	deriving (Functor, Applicative, Monad, MonadError RowError)++-- | Parse a single row.+parseRow :: RowParser a -> RowInput -> ExceptT RowError IO a+parseRow (RowParser parser) rowInfo =+	evalStateT (runReaderT parser rowInfo) (P.Col 0)++-- | Retrieve the current row number.+rowNumber :: RowParser P.Row+rowNumber = RowParser (asks (\ (RowInput _ row) -> row))++-- | Retrieve the current column number.+columnNumber :: RowParser P.Column+columnNumber = RowParser get++-- | Retrieve the number of columns left.+columnsLeft :: RowParser P.Column+columnsLeft = RowParser $ do+	RowInput (ResultInfo _ numCols) _ <- ask+	curCol <- get+	pure (numCols - curCol)++-- | Advance to next column without checking.+nextColumn :: RowParser ()+nextColumn =+	RowParser (modify (+ 1))++-- | Do something with the underlying result, row number and column number.+withColumn :: (P.Result -> P.Row -> P.Column -> RowParser a) -> RowParser a+withColumn action = do+	(RowInput (ResultInfo result numColumns) row, col) <- RowParser ((,) <$> ask <*> get)++	if col < numColumns then+		action result row col+	else+		throwError (RowError (RowErrorLocation col row) TooFewColumns)++-- | Fetch the 'TypedValue' associated with the current cell without advancing the cursor.+peekColumn :: RowParser TypedValue+peekColumn =+	withColumn $ \ result row col -> RowParser $ liftIO $+		TypedValue <$> P.ftype result col+		           <*> fmap (fmap Value) (P.getvalue' result row col)++-- | Fetch the type 'Oid' and value of the current cell. Also advances the cell cursor to the next+-- column.+fetchColumn :: RowParser TypedValue+fetchColumn =+	peekColumn <* nextColumn++-- | Fetch a column and parse it. Returning 'Nothing' from the provided parser function will cause+-- a 'ColumnRejected' to be raised. Advances the cell cursor to the next column.+parseColumn :: (TypedValue -> Maybe a) -> RowParser a+parseColumn proc = do+	typedValue <- peekColumn+	case proc typedValue of+		Just x  -> x <$ nextColumn+		Nothing -> do+			col <- columnNumber+			row <- rowNumber+			throwError (RowError (RowErrorLocation col row) (ColumnRejected typedValue))++-- | Fetch the cell's contents without moving the cell cursor.+peekContents :: RowParser (Maybe B.ByteString)+peekContents =+	withColumn (\ result row col -> RowParser (liftIO (P.getvalue' result row col)))++-- | Like 'peekContents' but moves the cell cursor to the next column.+fetchContents :: RowParser (Maybe B.ByteString)+fetchContents =+	peekContents <* nextColumn++-- | Parse the contents of a column (only if present). Returning 'Nothing' from the provided parser+-- function will raise a 'ContentsRejected'. When the cell is @NULL@, a 'ContentsRejected' is raised+-- aswell.+parseContents :: (B.ByteString -> Maybe a) -> RowParser a+parseContents proc = do+	value <- peekContents+	case value >>= proc of+		Just x  -> x <$ nextColumn+		Nothing -> do+			col <- columnNumber+			row <- rowNumber+			throwError (RowError (RowErrorLocation col row) (ContentsRejected value))++-- | Point cursor to the next column.+skipColumn :: RowParser ()+skipColumn = do+	(RowInput (ResultInfo _ numColumns) row, col) <- RowParser ((,) <$> ask <*> get)++	if col < numColumns then+		nextColumn+	else+		throwError (RowError (RowErrorLocation col row) TooFewColumns)++-- | Parse the result.+parseResult :: P.Result -> RowParser a -> ExceptT RowError IO [a]+parseResult result parser = do+	(resultInfo, numRows) <- liftIO $ do+		numColumns <- P.nfields result+		numRows <- P.ntuples result+		pure (ResultInfo result numColumns, numRows)++	forM [0 .. numRows - 1] (parseRow parser . RowInput resultInfo)
src/Database/PostgreSQL/Store/Table.hs view
@@ -1,444 +1,228 @@-{-# LANGUAGE TemplateHaskell, BangPatterns #-}+{-# LANGUAGE OverloadedStrings,+             ConstraintKinds,+             DataKinds,+             FlexibleContexts,+             FlexibleInstances,+             TypeFamilies,+             TypeOperators,+             TypeSynonymInstances,+             UndecidableInstances,+             ScopedTypeVariables,+             QuasiQuotes,+             DefaultSignatures+#-}  -- | -- Module:     Database.PostgreSQL.Store.Table--- Copyright:  (c) Ole Krüger 2015-2016+-- Copyright:  (c) Ole Krüger 2016 -- License:    BSD3 -- Maintainer: Ole Krüger <ole@vprsm.de> module Database.PostgreSQL.Store.Table (-	-- * Auxiliary data types-	Reference (..),+	-- * Columns+	Column (..),+	ColumnType (..),+	ColumnEntity (..), -	-- * Table types+	-- * Table 	Table (..),-	mkCreateQuery,--	-- * Table generation-	TableConstraint (..),-	mkTable-) where--import Control.Monad-import Control.Monad.Except--import Data.Int-import Data.List hiding (insert)-import Data.Proxy-import Data.String--import qualified Data.ByteString as B--import Language.Haskell.TH-import Language.Haskell.TH.Syntax--import Database.PostgreSQL.Store.Query-import Database.PostgreSQL.Store.Columns-import Database.PostgreSQL.Store.Result-import Database.PostgreSQL.Store.Errand---- | Reference a row of type @a@.-newtype Reference a = Reference { referenceID :: Int64 }-	deriving (Eq, Ord)--instance Show (Reference a) where-	show (Reference n) = show n--instance (QueryTable a) => Column (Reference a) where-	pack ref = pack (referenceID ref)--	unpack val = Reference <$> unpack val--	columnTypeName proxy =-		"BIGINT REFERENCES " ++ quoteIdentifier (tableName tableProxy) ++-		" (" ++ quoteIdentifier (tableIDName tableProxy) ++ ")"-		where-			tableProxy = (const Proxy :: Proxy (Reference a) -> Proxy a) proxy--	columnAllowNull _ = False--instance Result (Reference a) where-	queryResultProcessor = Reference <$> unpackColumn---- | Qualify @a@ as a table type. 'mkTable' can implement this class for you.-class Table a where-	-- | Insert a row into the table and return a 'Reference' to the inserted row.-	insert :: a -> Errand (Reference a)--	-- | Insert multiple rows into the table at once.-	insertMany :: [a] -> Errand [Reference a]-	insertMany = mapM insert--	-- | Find the row identified by the given reference.-	find :: Reference a -> Errand a--	-- | Update an existing row.-	update :: Reference a -> a -> Errand ()--	-- | Delete a row from the table.-	delete :: Reference a -> Errand ()--	-- | Generate the query which creates this table inside the database.-	-- Use 'mkCreateQuery' for convenience.-	createTableQuery :: Proxy a -> Query---- | Table field declaration-data TableField = TableField String Type Name---- | Table type declaration-data TableDec = TableDec Name Name [TableField] Pat---- | Generate an identifier for a table type.-tableIdentifier :: Name -> String-tableIdentifier name = quoteIdentifier (show name)---- | Make a comma-seperated list of identifiers which correspond to given fields.-tableFieldIdentifiers :: [TableField] -> String-tableFieldIdentifiers fields =-	intercalate ", " (map (\ (TableField name _ _) -> quoteIdentifier name) fields)---- | Beginning of a insert statement.-tableInsertStatementBegin :: TableDec -> String-tableInsertStatementBegin (TableDec table _ fields _) =-	"INSERT INTO " ++-	tableIdentifier table ++-	" (" ++-	tableFieldIdentifiers fields ++-	") VALUES "---- | End of a insert statement.-tableInsertStatementEnd :: String-tableInsertStatementEnd =-	" RETURNING \"$id\""---- | Call a constructor with some variables.-callConstructor :: Name -> [Name] -> Exp-callConstructor ctor params =-	foldl AppE (ConE ctor) (map VarE params)---- | Generate the list of selectors.-makeQuerySelectors :: [TableField] -> Q Exp-makeQuerySelectors fields =-	ListE <$> mapM (\ (TableField field _ _) -> [e| SelectorField $(stringE field) |]) fields---- | Generate the result processor for a table.-makeResultProcessor :: TableDec -> Q Exp-makeResultProcessor (TableDec _ ctor fields _) = do-	bindingNames <- mapM (\ (TableField name _ _) -> newName name) fields-	pure (DoE (map makeBinding bindingNames ++-	           [makeConstruction bindingNames]))-	where-		-- Bind expression 'name <- unpackColumn'-		makeBinding name =-			BindS (VarP name) (VarE 'unpackColumn)--		-- Last expression 'pure (ctor boundNames...)'-		makeConstruction names =-			NoBindS (AppE (VarE 'pure) (callConstructor ctor names))+	TableEntity (..),+	buildTableSchema, --- | Generate the create query.-makeCreateQuery :: TableDec -> [TableConstraint] -> Q Exp-makeCreateQuery (TableDec table _ fields _) constraints =-	runQueryBuilder $ do-		writeStringCode "CREATE TABLE IF NOT EXISTS " :: QueryBuilder [Q Exp] (Q Exp)-		writeIdentifier (show table)-		writeStringCode " ("+	insertColumns,+	insertColumnsOn, -		intercalateBuilder (writeStringCode ", ") $-			identDescription :-			map describeField fields ++-			map describeConstraint constraints+	GenericTable,+	describeGenericTable, -		writeStringCode ")"-	where-		identDescription = do-			writeIdentifier "$id"-			writeStringCode "BIGSERIAL NOT NULL PRIMARY KEY"+	-- * Helpers+	KColumns (..),+	KTable (..), -		describeField :: TableField -> QueryBuilder [Q Exp] (Q Exp)-		describeField (TableField name typ _) =-			writeCode [e| fromString (columnDescription (Proxy :: Proxy $(pure typ))-			                                            $(stringE (quoteIdentifier name))) |]+	GColumns (..),+	GTable (..), -		describeConstraint :: TableConstraint -> QueryBuilder [Q Exp] (Q Exp)-		describeConstraint (Unique names) = do-			writeStringCode "UNIQUE ("-			intercalateBuilder (writeStringCode ", ") $-				map (writeIdentifier . nameBase) names-			writeStringCode ")"+	AnalyzeRecordRep,+	AnalyzeTableRep, -		describeConstraint (Check code) = do-			writeStringCode "CHECK ("-			writeStringCode code-			writeStringCode ")"+	AnalyzeTable+) where --- | Generate the query which insert a row.-makeInsertQuery :: TableDec -> Q Exp-makeInsertQuery (TableDec table _ fields _) =-	runQueryBuilder $ do-		writeCode "INSERT INTO " :: QueryBuilder String (Q Exp)-		writeIdentifier (show table)-		writeCode " ("-		intercalateBuilder (writeCode ", ") $-			map (\ (TableField name _ _) -> writeIdentifier name) fields-		writeCode ") VALUES ("-		intercalateBuilder (writeCode ", ") $-			map (\ (TableField _ _ boundName) -> writeParam [e| pack $(varE boundName) |]) fields-		writeCode ") RETURNING "-		writeIdentifier "$id"+import           GHC.Generics+import           GHC.TypeLits --- | Generate the query which inserts multiple rows at once.-makeInsertManyQuery :: Name -> TableDec -> Q Exp-makeInsertManyQuery rows dec@(TableDec _ _ fields destructPat) =-	[e|-		let-			writeTuple $(pure destructPat) = do-				writeStringCode "(" :: QueryBuilder B.ByteString Value-				intercalateBuilder (writeStringCode ",") $(ListE <$> mapM packColumn fields)-				writeStringCode ")"-		in runQueryBuilder $ do-			writeStringCode $(stringE (tableInsertStatementBegin dec))-			intercalateBuilder (writeStringCode ",") (map writeTuple $(varE rows))-			writeStringCode $(stringE tableInsertStatementEnd)-	|]-	where-		packColumn (TableField _ _ boundName) = [e| writeColumn $(varE boundName) |]+import           Control.Monad --- | Generate the query for finding a row.-makeFindQuery :: Name -> TableDec -> Q Exp-makeFindQuery ref (TableDec table _ fields _) =-	runQueryBuilder $ do-		writeCode "SELECT " :: QueryBuilder String (Q Exp)-		intercalateBuilder (writeCode ", ") $-			map (\ (TableField name _ _) -> writeIdentifier name) fields-		writeCode " FROM "-		writeIdentifier (show table)-		writeCode " WHERE "-		writeIdentifier "$id"-		writeCode " = "-		writeParam [e| pack $(varE ref) |]+import           Data.Kind+import           Data.Proxy --- | Generate the query for updating a row.-makeUpdateQuery :: Name -> TableDec -> Q Exp-makeUpdateQuery ref (TableDec table _ fields _) =-	runQueryBuilder $ do-		writeCode "UPDATE " :: QueryBuilder String (Q Exp)-		writeIdentifier (show table)-		writeCode " SET "-		intercalateBuilder (writeCode ", ") $-			flip map fields $ \ (TableField name _ boundName) -> do-				writeIdentifier name-				writeCode " = "-				writeParam [e| pack $(varE boundName) |]-		writeCode " WHERE "-		writeIdentifier "$id"-		writeCode " = "-		writeParam [e| pack $(varE ref) |]+import qualified Data.ByteString as B --- | Generate the query for deleting a row.-makeDeleteQuery :: Name -> Name -> Q Exp-makeDeleteQuery ref table =-	runQueryBuilder $ do-		writeCode "DELETE FROM " :: QueryBuilder String (Q Exp)-		writeIdentifier (show table)-		writeCode " WHERE "-		writeIdentifier "$id"-		writeCode " = "-		writeParam [e| pack $(varE ref) |]+import           Database.PostgreSQL.Store.Entity+import           Database.PostgreSQL.Store.Utilities+import           Database.PostgreSQL.Store.ColumnEntity+import           Database.PostgreSQL.Store.Query.Builder --- | Implement relevant instances for the given table type.-implementClasses :: TableDec -> [TableConstraint] -> Q [Dec]-implementClasses dec@(TableDec table _ fields destructPat) constraints =-	[d|-		instance QueryTable $(conT table) where-			tableName _      = $(stringE (show table))-			tableIDName _    = "$id"-			tableSelectors _ = $(makeQuerySelectors fields)+-- | Type-level description of a record+data KColumns+	= TCombine KColumns KColumns+	| TSelector Symbol Type -		instance Result $(conT table) where-			queryResultProcessor = $(makeResultProcessor dec)+-- | Desciption of a column+data Column = Column {+	-- | Column name+	colName :: B.ByteString, -		instance Table $(conT table) where-			insert $(pure destructPat) = do-				rs <- query $(makeInsertQuery dec)-				case rs of-					(ref : _) -> pure ref-					_         -> throwError EmptyResult+	-- | Column type+	colType :: ColumnType+} -			insertMany [] = pure []-			insertMany rows =-				query $(makeInsertManyQuery 'rows dec)+-- | Provide the means to demote 'KColumns' to a value.+class GColumns (rec :: KColumns) where+	-- | Instantiate singleton+	gDescribeColumns :: proxy rec -> [Column] -			find ref = do-				rs <- query $(makeFindQuery 'ref dec)-				case rs of-					(row : _) -> pure row-					_         -> throwError EmptyResult+instance (KnownSymbol name, ColumnEntity typ) => GColumns ('TSelector name typ) where+	gDescribeColumns _ =+		[Column (buildByteString (symbolVal (Proxy :: Proxy name)))+		        (describeColumnType (Proxy :: Proxy typ))] -			update ref $(pure destructPat) =-				query_ $(makeUpdateQuery 'ref dec)+instance (GColumns lhs, GColumns rhs) => GColumns ('TCombine lhs rhs) where+	gDescribeColumns _ =+		gDescribeColumns (Proxy :: Proxy lhs)+		++ gDescribeColumns (Proxy :: Proxy rhs) -			delete ref =-				query_ $(makeDeleteQuery 'ref table)+-- | Check the 'Generic' representation of a record in order to generate an instance of 'KColumns'.+type family AnalyzeRecordRep org (rec :: * -> *) :: KColumns where+	-- Single record field+	AnalyzeRecordRep org (S1 ('MetaSel ('Just name) m1 m2 m3) (Rec0 typ)) =+		'TSelector name typ -			createTableQuery _ =-				$(makeCreateQuery dec constraints)-	|]+	-- Non-record field+	AnalyzeRecordRep org (S1 ('MetaSel 'Nothing m1 m2 m3) a) =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " must have a single record constructor") --- | Check that each field's type has an implementation of 'Column'.-checkRecordFields :: [VarBangType] -> Q [TableField]-checkRecordFields fields =-	forM fields $ \ (name, _, typ) -> do-		ii <- isInstance ''Column [typ]-		unless ii $-			fail ("Type of field '" ++ show name ++ "' ('" ++ show typ ++-			      "') type does not implement '" ++ show ''Column ++ "'")+	-- Multiple fields+	AnalyzeRecordRep org (lhs :*: rhs) =+		'TCombine (AnalyzeRecordRep org lhs) (AnalyzeRecordRep org rhs) -		TableField (nameBase name) typ <$> newName (nameBase name)+	-- Missing field(s)+	AnalyzeRecordRep org U1 =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " has one constructor, therefore that constructor must have \+		                       \at least one field") --- | Check that each constructor parameter type implements 'Column'.-checkNormalFields :: [BangType] -> Q [TableField]-checkNormalFields fields = do-	forM (fields `zip` [1 .. length fields]) $ \ ((_, typ), idx) -> do-		ii <- isInstance ''Column [typ]-		unless ii $-			fail ("Type of constructor parameter #" ++ show idx ++ " ('" ++ show typ ++-			      "') type does not implement '" ++ show ''Column ++ "'")+	-- Something else+	AnalyzeRecordRep org other =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " has a constructor with an invalid selector"+		           ':$$: 'ShowType other) -		TableField ("column" ++ show idx) typ <$> newName ("column" ++ show idx)+-- | Type-level description of a table+data KTable = TTable Symbol KColumns --- |-makeCtorPattern :: Name -> [TableField] -> Pat-makeCtorPattern ctor fields =-	ConP ctor (map (\ (TableField _ _ name) -> VarP name) fields)+-- | Description of a table+data Table = Table {+	-- | Table name+	tableName :: B.ByteString, --- | Verify that the given constructor is viable and construct a 'TableDec'.-checkTableCtor :: Name -> Con -> Q TableDec-checkTableCtor table (RecC ctor ctorFields) = do-	when (length ctorFields < 1)-	     (fail ("'" ++ show ctor ++ "' must have at least one field"))+	-- | Table columns+	tableCols :: [Column]+} -	fields <- checkRecordFields ctorFields-	pure (TableDec table ctor fields (makeCtorPattern ctor fields))+-- | Provide the means to demote 'KTable' to a value.+class GTable (tbl :: KTable) where+	-- | Instantiate singleton+	gDescribeTable :: proxy tbl -> Table -checkTableCtor table (NormalC ctor ctorFields) = do-	when (length ctorFields < 1)-	     (fail ("'" ++ show ctor ++ "' must have at least one field"))+instance (KnownSymbol name, GColumns cols) => GTable ('TTable name cols) where+	gDescribeTable _ =+		Table (buildByteString (symbolVal (Proxy :: Proxy name)))+		      (gDescribeColumns (Proxy :: Proxy cols)) -	fields <- checkNormalFields ctorFields-	pure (TableDec table ctor fields (makeCtorPattern ctor fields))+-- | Check the 'Generic' representation of a data type in order to generate an instance of 'KTable'.+type family AnalyzeTableRep org (dat :: * -> *) :: KTable where+	-- Single constructor+	AnalyzeTableRep org (D1 meta1 (C1 ('MetaCons name f 'True) sel)) =+		'TTable name (AnalyzeRecordRep org sel) -checkTableCtor table _ =-	fail ("'" ++ show table ++ "' must have a normal or record constructor")+	-- Data type with constructor(s) that does not match the given patterns+	AnalyzeTableRep org (D1 meta other) =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " must have a single record constructor") --- | Make sure the given declaration can be used, then construct a 'TableDec'.-checkTableDec :: Name -> Dec -> Q TableDec-checkTableDec _ (DataD ctx tableName typeVars kind ctorNames _) = do-	when (length ctx > 0)-	     (fail ("'" ++ show tableName ++ "' must not have a context"))+	-- Something else+	AnalyzeTableRep org other =+		TypeError ('Text "Given type "+		           ':<>: 'ShowType org+		           ':<>: 'Text " is not a valid data type"+		           ':$$: 'ShowType other) -	when (length typeVars > 0)-	     (fail ("'" ++ show tableName ++ "' must not use type variables"))+-- | Analyzes a type in order to retrieve its 'KTable' representation.+type AnalyzeTable a = AnalyzeTableRep a (Rep a) -	when (length ctorNames /= 1)-	     (fail ("'" ++ show tableName ++ "' must have 1 constructor"))+-- | Constraint for generic tables+type GenericTable a = (Generic a, GTable (AnalyzeTable a)) -	when (kind /= Nothing && kind /= Just StarT)-	     (fail ("'" ++ show tableName ++ "' must have kind *"))+-- | Fetch the table description for a generic table type.+describeGenericTable :: (GenericTable a) => proxy a -> Table+describeGenericTable proxy =+	gDescribeTable ((const Proxy :: proxy a -> Proxy (AnalyzeTable a)) proxy) -	let [ctorName] = ctorNames+-- | Classify a type which can be used as a table.+class (Entity a) => TableEntity a where+	-- | Describe the table type.+	describeTableType :: proxy a -> Table -	checkTableCtor tableName ctorName+	default describeTableType :: (GenericTable a) => proxy a -> Table+	describeTableType = describeGenericTable -checkTableDec tableName _ =-	fail ("'" ++ show tableName ++ "' must declare a data type")+-- | Build SQL code which describes the column.+buildColumn :: Column -> QueryBuilder+buildColumn (Column name (ColumnType typeName notNull mbCheck)) = do+	insertName name+	insertCode " "+	insertName typeName --- | Options to 'mkTable'.-data TableConstraint-	= Unique [Name]-	  -- ^ A combination of fields must be unique.-	  --   @Unique ['name1, 'name2, ...]@ works analogous to the table constraint-	  --   @UNIQUE (name1, name2, ...)@ in SQL.-	| Check String-	  -- ^ The given statement must evaluate to true. Just like @CHECK (statement)@ in SQL.-	deriving (Show, Eq, Ord)+	when notNull (insertCode " NOT NULL") --- | Implement the type classes 'QueryTable', 'Table' and 'Result' for the given type.------ The given type must fulfill these requirements:------   * Data type---   * No type context---   * No type variables---   * One record constructor with 1 or more fields---   * All field types must have an instance of 'Column'------ Example:------ @--- {-\# LANGUAGE TemplateHaskell, QuasiQuotes \#-}--- module Movies where------ ...------ data Movie = Movie {---     movieTitle :: 'String',---     movieYear  :: 'Int'--- } deriving 'Show'------ 'mkTable' ''Movie []------ data Actor = Actor {---     actorName :: 'String',---     actorAge  :: 'Int'--- } deriving 'Show'------ 'mkTable' ''Actor ['Unique' ['actorName], 'Check' ['pgss'| actorAge >= 18 |]]------ data MovieCast = MovieCast {---     movieCastMovie :: 'Reference' Movie,---     movieCastActor :: 'Reference' Actor--- } deriving 'Show'------ 'mkTable' ''MovieCast ['Unique' ['movieCastMovie, 'movieCastActor]]--- @------ In this example, 'Reference' takes care of adding the @FOREIGN KEY@ constraint, so we don't have--- to.----mkTable :: Name -> [TableConstraint] -> Q [Dec]-mkTable name constraints = do-	info <- reify name-	case info of-		TyConI dec -> do-			tableDec <- checkTableDec name dec-			implementClasses tableDec constraints+	case mbCheck of+		Just gen -> do+			insertCode " CHECK("+			gen name+			insertCode ")" -		_ -> fail ("'" ++ show name ++ "' is not a type constructor")+		Nothing -> pure () --- | Check if the given name refers to a type.-isType :: Name -> Q Bool-isType name = do-	info <- reify name-	pure $ case info of-		TyConI _ -> True-		_        -> False+-- | Build the SQL code which describes and creates the table.+buildTableSchema :: Table -> QueryBuilder+buildTableSchema (Table name cols) = do+	insertCode "CREATE TABLE IF NOT EXISTS "+	insertName name+	insertCode "("+	insertCommaSeperated (map buildColumn cols)+	insertCode ")" --- | Generate a 'Query' expression which will create the table described by the given type.------ Example:------ @--- data Table = Table { myField :: Int }--- 'mkTable' ''Table []--- ...--- 'query_' $('mkCreateQuery' ''Table)--- @----mkCreateQuery :: Name -> Q Exp-mkCreateQuery name = do-	-- Is the given name a type?-	it <- isType name-	unless it (fail "Given name does not refer to a type.")+-- | Insert a comma-seperated list of the fully qualified column names of a table.+insertColumns :: Table -> QueryBuilder+insertColumns (Table name cols) =+	insertCommaSeperated (map insertColumn cols)+	where+		insertColumn (Column colName _) = do+			insertName name+			insertCode "."+			insertName colName -	-- Actual splice-	[e| createTableQuery (Proxy :: Proxy $(pure (ConT name))) |]+-- | Similar to 'insertColumns', but instead it expands the column names on an alias.+insertColumnsOn :: Table -> B.ByteString -> QueryBuilder+insertColumnsOn (Table _ cols) name =+	insertCommaSeperated (map insertColumn cols)+	where+		insertColumn (Column colName _) = do+			insertName name+			insertCode "."+			insertName colName
+ src/Database/PostgreSQL/Store/Types.hs view
@@ -0,0 +1,28 @@+-- |+-- Module:     Database.PostgreSQL.Store.Types+-- Copyright:  (c) Ole Krüger 2016+-- License:    BSD3+-- Maintainer: Ole Krüger <ole@vprsm.de>+module Database.PostgreSQL.Store.Types (+	-- * General+	Value (..),+	TypedValue (..),+	Query (..)+) where++import qualified Data.ByteString           as B+import qualified Database.PostgreSQL.LibPQ as P++-- | Value of a cell in the result set+newtype Value = Value { valueData :: B.ByteString }+	deriving (Show, Eq, Ord)++-- | Value and type 'P.Oid' of a cell in the result set+data TypedValue = TypedValue P.Oid (Maybe Value)+	deriving (Show, Eq, Ord)++-- | Query+data Query a = Query {+	queryStatement :: B.ByteString,+	queryParams    :: [TypedValue]+} deriving (Show, Eq, Ord)
+ src/Database/PostgreSQL/Store/Utilities.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE TemplateHaskell #-}++-- |+-- Module:     Database.PostgreSQL.Store.Utilities+-- Copyright:  (c) Ole Krüger 2016+-- License:    BSD3+-- Maintainer: Ole Krüger <ole@vprsm.de>+module Database.PostgreSQL.Store.Utilities (+	showByteString,+	readByteString,+	buildByteString,+	liftByteString+) where++import           Language.Haskell.TH+import           Language.Haskell.TH.Syntax++import qualified Data.ByteString                    as B+import qualified Blaze.ByteString.Builder           as B+import qualified Blaze.ByteString.Builder.Char.Utf8 as B+import qualified Data.Text                          as T+import qualified Data.Text.Encoding                 as T++import           Text.Read (readMaybe)++-- | Show as 'ByteString'+showByteString :: (Show a) => a -> B.ByteString+showByteString =+	B.toByteString . B.fromString . show++-- | Read as 'ByteString'+readByteString :: (Read a) => B.ByteString -> Maybe a+readByteString =+	readMaybe . T.unpack . T.decodeUtf8++-- | UTF-8 correct alternative to 'fromString'.+buildByteString :: String -> B.ByteString+buildByteString =+	B.toByteString . B.fromString++-- | Lift 'ByteString'.+liftByteString :: B.ByteString -> Q Exp+liftByteString bs =+	[e| B.pack $(lift (B.unpack bs)) |]
− tests/Main.hs
@@ -1,29 +0,0 @@-module Main (main) where--import           Test.Hspec-import           Test.Database.PostgreSQL.Store--import           Data.String--import qualified Database.PostgreSQL.LibPQ as P--import           System.Environment--connectMaybe :: String -> IO (Maybe P.Connection)-connectMaybe info = do-	con <- P.connectdb (fromString info)-	status <- P.status con--	pure $-		if status == P.ConnectionOk then-			Just con-		else-			Nothing---- | Test entry point-main :: IO ()-main = do-	mbInfo <- lookupEnv "PGINFO"-	mbCon <- maybe (pure Nothing) connectMaybe mbInfo--	hspec (allSpecs mbCon)
− tests/Test/Database/PostgreSQL/Store.hs
@@ -1,17 +0,0 @@-module Test.Database.PostgreSQL.Store (-	allSpecs-) where--import           Test.Hspec--import           Test.Database.PostgreSQL.Store.Columns-import           Test.Database.PostgreSQL.Store.Query-import           Test.Database.PostgreSQL.Store.Table--import qualified Database.PostgreSQL.LibPQ as P--allSpecs :: Maybe P.Connection -> Spec-allSpecs mbCon = do-	allColumnsSpecs mbCon-	allTableSpecs mbCon-	allQuerySpecs
− tests/Test/Database/PostgreSQL/Store/Columns.hs
@@ -1,117 +0,0 @@-{-# LANGUAGE OverloadedStrings, TemplateHaskell, QuasiQuotes #-}--module Test.Database.PostgreSQL.Store.Columns (-	allColumnsSpecs-) where--import           Test.Hspec-import           Test.QuickCheck-import           Test.QuickCheck.Monadic--import           Control.Monad.Trans--import           Data.Int-import           Data.Word-import           Data.Proxy--import           Database.PostgreSQL.Store.Query-import           Database.PostgreSQL.Store.Columns-import           Database.PostgreSQL.Store.Result-import           Database.PostgreSQL.Store.Errand--import qualified Database.PostgreSQL.LibPQ as P--testLivePackUnpack :: (Column a, Arbitrary a, Eq a, Show a) => Proxy a -> P.Connection -> Property-testLivePackUnpack proxy con =-	property $ coerceParam proxy $ \ value -> monadicIO $ do-		lift $ do-			res <- runErrand con (queryWith (Query "SELECT $1" [pack value]) unpackColumn)--			shouldBe res (Right [value])-	where-		coerceParam :: Proxy a -> (a -> b) -> a -> b-		coerceParam _ f x = f x--testPackUnpack :: (Column a, Arbitrary a, Eq a, Show a) => Proxy a -> Property-testPackUnpack proxy =-	property (compare proxy)-	where-		compare :: (Column a, Eq a) => Proxy a -> a -> Bool-		compare _ x = unpack (pack x) == Just x--allColumnsSpecs :: Maybe P.Connection -> Spec-allColumnsSpecs mbCon = do-	describe "Database.PostgreSQL.Store.Columns" $ do-		describe "instance (Column a) => Column (Maybe a)" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy (Maybe Bool)))--		describe "instance Column Bool" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy Bool))--		describe "instance Column Int" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy Int))--		describe "instance Column Int8" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy Int8))--		describe "instance Column Int16" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy Int16))--		describe "instance Column Int32" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy Int32))--		describe "instance Column Int64" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy Int64))--		describe "instance Column Word" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy Word))--		describe "instance Column Word8" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy Word8))--		describe "instance Column Word16" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy Word16))--		describe "instance Column Word32" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy Word32))--		describe "instance Column Word64" $-			it "behaves isomorphic" (testPackUnpack (Proxy :: Proxy Word64))--	maybe (pure ()) liveSpecs mbCon--liveSpecs :: P.Connection -> Spec-liveSpecs con =-	describe "Database.PostgreSQL.Store.Columns (live)" $ do-		describe "instance Column Bool" $-			it "behaves isomorphic" (testLivePackUnpack (Proxy :: Proxy Bool) con)--		describe "instance Column Int" $-			it "behaves isomorphic" (testLivePackUnpack (Proxy :: Proxy Int) con)--		describe "instance Column Int8" $-			it "behaves isomorphic" (testLivePackUnpack (Proxy :: Proxy Int8) con)--		describe "instance Column Int16" $-			it "behaves isomorphic" (testLivePackUnpack (Proxy :: Proxy Int16) con)--		describe "instance Column Int32" $-			it "behaves isomorphic" (testLivePackUnpack (Proxy :: Proxy Int32) con)--		describe "instance Column Int64" $-			it "behaves isomorphic" (testLivePackUnpack (Proxy :: Proxy Int64) con)--		describe "instance Column Word" $-			it "behaves isomorphic" (testLivePackUnpack (Proxy :: Proxy Word) con)--		describe "instance Column Word8" $-			it "behaves isomorphic" (testLivePackUnpack (Proxy :: Proxy Word8) con)--		describe "instance Column Word16" $-			it "behaves isomorphic" (testLivePackUnpack (Proxy :: Proxy Word16) con)--		describe "instance Column Word32" $-			it "behaves isomorphic" (testLivePackUnpack (Proxy :: Proxy Word32) con)--		describe "instance Column Word64" $-			it "behaves isomorphic" (testLivePackUnpack (Proxy :: Proxy Word64) con)
− tests/Test/Database/PostgreSQL/Store/Query.hs
@@ -1,92 +0,0 @@-{-# LANGUAGE OverloadedStrings, TemplateHaskell, QuasiQuotes #-}--module Test.Database.PostgreSQL.Store.Query (-	allQuerySpecs-) where--import Test.Hspec--import Database.PostgreSQL.Store.Query-import Database.PostgreSQL.Store.Columns--data TestTable = TestTable-	deriving (Show, Eq, Ord)--instance QueryTable TestTable where-	tableName _      = "T"-	tableIDName _    = "a"-	tableSelectors _ = [SelectorField "b", SelectorField "c"]--testVariable :: Int-testVariable = 1337--allQuerySpecs :: Spec-allQuerySpecs =-	describe "Database.PostgreSQL.Store.Query" $-		describe "pgsq" $ do-			it "resolves table names" $ do-				shouldBe [pgsq|@TestTable|] (Query "\"T\"" [])-				shouldBe [pgsq|@Test.Database.PostgreSQL.Store.Query.TestTable|] [pgsq|@TestTable|]-				shouldBe [pgsq|@TestTable.b|] (Query "\"T\".b" [])-				shouldBe [pgsq|@TestTable."b"|] (Query "\"T\".\"b\"" [])--			it "resolves identifier fields" $ do-				shouldBe [pgsq|&TestTable|] (Query "\"T\".\"a\"" [])-				shouldBe [pgsq|&Test.Database.PostgreSQL.Store.Query.TestTable|] [pgsq|&TestTable|]--			it "resolves selectors" $ do-				shouldBe [pgsq|#TestTable|] (Query "\"T\".\"b\", \"T\".\"c\"" [])-				shouldBe [pgsq|#TestTable|] [pgsq|#Test.Database.PostgreSQL.Store.Query.TestTable|]--			it "embeds variables" $ do-				let i = testVariable-				shouldBe [pgsq|$testVariable $i|] (Query "$1 $2" [pack testVariable, pack i])--			it "ignores operator #" $ do-				shouldBe [pgsq|1 # 2|]       (Query "1 # 2" [])-				shouldBe [pgsq|a # b|]       (Query "a # b" [])-				shouldBe [pgsq|a # T|]       (Query "a # T" [])-				shouldBe [pgsq|a # T.a|]     (Query "a # T.a" [])-				shouldBe [pgsq|a # "T"|]     (Query "a # \"T\"" [])-				shouldBe [pgsq|a # "T"."a"|] (Query "a # \"T\".\"a\"" [])-				shouldBe [pgsq|1#2|]         (Query "1#2" [])-				shouldBe [pgsq|a#b|]         (Query "a#b" [])-				shouldBe [pgsq|a#(T)|]       (Query "a#(T)" [])-				shouldBe [pgsq|a#(T.a)|]     (Query "a#(T.a)" [])-				shouldBe [pgsq|a#"T"|]       (Query "a#\"T\"" [])-				shouldBe [pgsq|a#"T"."a"|]   (Query "a#\"T\".\"a\"" [])--			it "ignores operator &" $ do-				shouldBe [pgsq|1 & 2|]       (Query "1 & 2" [])-				shouldBe [pgsq|a & b|]       (Query "a & b" [])-				shouldBe [pgsq|a & T|]       (Query "a & T" [])-				shouldBe [pgsq|a & T.a|]     (Query "a & T.a" [])-				shouldBe [pgsq|a & "T"|]     (Query "a & \"T\"" [])-				shouldBe [pgsq|a & "T"."a"|] (Query "a & \"T\".\"a\"" [])-				shouldBe [pgsq|1&2|]         (Query "1&2" [])-				shouldBe [pgsq|a&b|]         (Query "a&b" [])-				shouldBe [pgsq|a&(T)|]       (Query "a&(T)" [])-				shouldBe [pgsq|a&(T.a)|]     (Query "a&(T.a)" [])-				shouldBe [pgsq|a&"T"|]       (Query "a&\"T\"" [])-				shouldBe [pgsq|a&"T"."a"|]   (Query "a&\"T\".\"a\"" [])--			it "ignores operator @" $ do-				shouldBe [pgsq|@-5|]      (Query "@-5" [])-				shouldBe [pgsq|@(-5)|]    (Query "@(-5)" [])-				shouldBe [pgsq|@a|]       (Query "@a" [])-				shouldBe [pgsq|@(a)|]     (Query "@(a)" [])-				shouldBe [pgsq|@(T)|]     (Query "@(T)" [])-				shouldBe [pgsq|@(T.a)|]   (Query "@(T.a)" [])-				shouldBe [pgsq|@"a"|]     (Query "@\"a\"" [])-				shouldBe [pgsq|@"T"|]     (Query "@\"T\"" [])-				shouldBe [pgsq|@"T"."a"|] (Query "@\"T\".\"a\"" [])--			it "ignores bad use of $" $ do-				shouldBe [pgsq|$1|]       (Query "$1" [])-				shouldBe [pgsq|$(1)|]     (Query "$(1)" [])-				shouldBe [pgsq|$(a)|]     (Query "$(a)" [])-				shouldBe [pgsq|$(T)|]     (Query "$(T)" [])-				shouldBe [pgsq|$(T.a)|]   (Query "$(T.a)" [])-				shouldBe [pgsq|$"a"|]     (Query "$\"a\"" [])-				shouldBe [pgsq|$"T"|]     (Query "$\"T\"" [])-				shouldBe [pgsq|$"T"."a"|] (Query "$\"T\".\"a\"" [])
− tests/Test/Database/PostgreSQL/Store/Table.hs
@@ -1,140 +0,0 @@-{-# LANGUAGE OverloadedStrings, TemplateHaskell, QuasiQuotes #-}--module Test.Database.PostgreSQL.Store.Table (-	allTableSpecs-) where--import           Test.Hspec--import           Data.Proxy--import           Database.PostgreSQL.Store.Table-import           Database.PostgreSQL.Store.Query-import           Database.PostgreSQL.Store.Errand--import qualified Database.PostgreSQL.LibPQ as P--data OnlyEvens = OnlyEvens {-	value :: Int-} deriving (Show, Eq)--mkTable ''OnlyEvens-	[Unique ['value],-	 Check [pgss| value % 2 = 0 |]]--allTableSpecs :: Maybe P.Connection -> Spec-allTableSpecs mbCon = do-	maybe (pure ()) liveSpecs mbCon--dropTable :: P.Connection -> IO ()-dropTable con =-	() <$ runErrand con (query_ [pgsq| DROP TABLE @OnlyEvens |])--clearTable :: P.Connection -> IO ()-clearTable con =-	() <$ runErrand con (query_ [pgsq| DELETE FROM @OnlyEvens |])--liveSpecs :: P.Connection -> Spec-liveSpecs con =-	describe "Database.PostgreSQL.Store.Table (live)" $-		afterAll_ (dropTable con) $ describe "auto-generated instance Table" $-			after_ (clearTable con) $ do-				it "creates tables" $ do-					res1 <- runErrand con $-						query_ $(mkCreateQuery ''OnlyEvens)-					shouldBe res1 (Right ())--					-- Table exists now-					let table = tableName (Proxy :: Proxy OnlyEvens)-					res2 <- runErrand con $-						query [pgsq| SELECT COUNT(*) FROM pg_tables WHERE tablename = $table |]-					shouldBe res2 (Right [Single (1 :: Int)])--				it "inserts a rows" $ do-					res1 <- runErrand con (insert (OnlyEvens 2))-					shouldSatisfy res1 $ \ r ->-						case r of-							Right (Reference _) -> True-							_                   -> False--					-- Has been inserted correctly-					res2 <- runErrand con $-						query [pgsq| SELECT #OnlyEvens FROM @OnlyEvens |]-					shouldBe res2 (Right [OnlyEvens 2])--				it "respects constraints while inserting" $ do-					-- Violate UNIQUE-					res3 <- runErrand con $ do-						insert (OnlyEvens 2)-						insert (OnlyEvens 2)-					shouldSatisfy res3 $ \ r ->-						case r of-							Left (ExecError _ UniqueViolation _ _ _) -> True-							_                          -> False--					-- Violate CHECK-					res4 <- runErrand con (insert (OnlyEvens 1))-					shouldSatisfy res4 $ \ r ->-						case r of-							Left (ExecError _ CheckViolation _ _ _) -> True-							_                         -> False--				it "inserts many rows" $ do-					res1 <- runErrand con (insertMany (map OnlyEvens [2, 4 .. 100]))-					shouldSatisfy res1 $ \ r ->-						case r of-							Right xs -> length xs == 50-							_        -> False--					-- Verify that there are actually 50 present-					res2 <- runErrand con $-						query [pgsq| SELECT COUNT(*) FROM @OnlyEvens |]-					shouldBe res2 (Right [Single (50 :: Int)])--				it "finds a row" $ do-					res1 <- runErrand con $ do-						ref <- insert (OnlyEvens 4)-						find ref-					shouldBe res1 (Right (OnlyEvens 4))--				it "finds many rows" $-					pendingWith "Not yet implemented"--				it "updates a row" $ do-					res1 <- runErrand con $ do-						ref <- insert (OnlyEvens 4)-						update ref (OnlyEvens 6)-						find ref-					shouldBe res1 (Right (OnlyEvens 6))--				it "respects constraints while updating" $ do-					-- Violate UNIQUE-					res1 <- runErrand con $ do-						insert (OnlyEvens 2)-						ref <- insert (OnlyEvens 4)-						update ref (OnlyEvens 2)-					shouldSatisfy res1 $ \ r ->-						case r of-							Left (ExecError _ UniqueViolation _ _ _) -> True-							_                          -> False--					-- Vioate CHECK-					res2 <- runErrand con $ do-						ref <- insert (OnlyEvens 6)-						update ref (OnlyEvens 7)-					shouldSatisfy res2 $ \ r ->-						case r of-							Left (ExecError _ CheckViolation _ _ _) -> True-							_                         -> False--				it "updates many rows" $-					pendingWith "Not yet implemented"--				it "deletes a row" $ do-					res1 <- runErrand con $ do-						ref <- insert (OnlyEvens 2)-						delete ref-					shouldBe res1 (Right ())--				it "deletes many rows" $-					pendingWith "Not yet implemented"