packages feed

opaleye 0.3.1.2 → 0.4.0.0

raw patch · 41 files changed

+1277/−349 lines, 41 filesdep +QuickCheckdep +containersdep +voiddep ~contravariantdep ~product-profunctorsdep ~semigroupsPVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck, containers, void

Dependency ranges changed: contravariant, product-profunctors, semigroups

API changes (from Hackage documentation)

- Opaleye.Internal.Binary: data Binaryspec columns columns'
- Opaleye.Internal.Distinct: data Distinctspec a b
- Opaleye.Internal.HaskellDB.Sql: type SqlTable = String
- Opaleye.Internal.Join: data NullMaker a b
- Opaleye.Internal.Order: SingleOrder :: OrderOp -> (a -> PrimExpr) -> SingleOrder a
- Opaleye.Internal.Order: data SingleOrder a
- Opaleye.Internal.Order: instance Contravariant SingleOrder
- Opaleye.Internal.PackMap: over :: PackMap a b s t -> (a -> b) -> s -> t
- Opaleye.Internal.PackMap: packmap :: Applicative f => PackMap a b s t -> (a -> f b) -> s -> f t
- Opaleye.Internal.Sql: jAttrs :: Join -> [(SqlExpr, Maybe SqlColumn)]
- Opaleye.Internal.Table: data Writer columns dummy
- Opaleye.Internal.Tag: data Tag
- Opaleye.Internal.Values: data Valuesspec columns columns'
+ Opaleye.Aggregate: arrayAgg :: Aggregator (Column a) (Column (PGArray a))
+ Opaleye.Aggregate: stringAgg :: Column PGText -> Aggregator (Column PGText) (Column PGText)
+ Opaleye.Column: unsafeCoerceColumn :: Column a -> Column b
+ Opaleye.Internal.Aggregate: instance SumProfunctor Aggregator
+ Opaleye.Internal.Binary: newtype Binaryspec columns columns'
+ Opaleye.Internal.Column: unsafeCoerceColumn :: Column a -> Column b
+ Opaleye.Internal.Distinct: instance SumProfunctor Distinctspec
+ Opaleye.Internal.Distinct: newtype Distinctspec a b
+ Opaleye.Internal.HaskellDB.PrimQuery: AggrArr :: AggrOp
+ Opaleye.Internal.HaskellDB.PrimQuery: AggrStringAggr :: PrimExpr -> AggrOp
+ Opaleye.Internal.HaskellDB.PrimQuery: DefaultInsertExpr :: PrimExpr
+ Opaleye.Internal.HaskellDB.PrimQuery: NullsFirst :: OrderNulls
+ Opaleye.Internal.HaskellDB.PrimQuery: NullsLast :: OrderNulls
+ Opaleye.Internal.HaskellDB.PrimQuery: OrderOp :: OrderDirection -> OrderNulls -> OrderOp
+ Opaleye.Internal.HaskellDB.PrimQuery: data OrderDirection
+ Opaleye.Internal.HaskellDB.PrimQuery: data OrderNulls
+ Opaleye.Internal.HaskellDB.PrimQuery: instance Show OrderDirection
+ Opaleye.Internal.HaskellDB.PrimQuery: instance Show OrderNulls
+ Opaleye.Internal.HaskellDB.PrimQuery: orderDirection :: OrderOp -> OrderDirection
+ Opaleye.Internal.HaskellDB.PrimQuery: orderNulls :: OrderOp -> OrderNulls
+ Opaleye.Internal.HaskellDB.Sql: DefaultSqlExpr :: SqlExpr
+ Opaleye.Internal.HaskellDB.Sql: SqlNullsFirst :: SqlOrderNulls
+ Opaleye.Internal.HaskellDB.Sql: SqlNullsLast :: SqlOrderNulls
+ Opaleye.Internal.HaskellDB.Sql: SqlOrder :: SqlOrderDirection -> SqlOrderNulls -> SqlOrder
+ Opaleye.Internal.HaskellDB.Sql: SqlTable :: String -> SqlTable
+ Opaleye.Internal.HaskellDB.Sql: data SqlOrderDirection
+ Opaleye.Internal.HaskellDB.Sql: data SqlOrderNulls
+ Opaleye.Internal.HaskellDB.Sql: instance Show SqlOrderDirection
+ Opaleye.Internal.HaskellDB.Sql: instance Show SqlOrderNulls
+ Opaleye.Internal.HaskellDB.Sql: instance Show SqlTable
+ Opaleye.Internal.HaskellDB.Sql: newtype SqlTable
+ Opaleye.Internal.HaskellDB.Sql: sqlOrderDirection :: SqlOrder -> SqlOrderDirection
+ Opaleye.Internal.HaskellDB.Sql: sqlOrderNulls :: SqlOrder -> SqlOrderNulls
+ Opaleye.Internal.HaskellDB.Sql.Default: toSqlColumn :: Attribute -> SqlColumn
+ Opaleye.Internal.HaskellDB.Sql.Print: ppTable :: SqlTable -> Doc
+ Opaleye.Internal.Helpers: (.::.) :: (r -> z) -> (a -> b -> c -> d -> e -> r) -> a -> b -> c -> d -> e -> z
+ Opaleye.Internal.Join: newtype NullMaker a b
+ Opaleye.Internal.Order: instance Decidable Order
+ Opaleye.Internal.Order: instance Divisible Order
+ Opaleye.Internal.PGTypes: castToType :: Name -> String -> Column c
+ Opaleye.Internal.PGTypes: lazyDecodeUtf8 :: ByteString -> String
+ Opaleye.Internal.PGTypes: literalColumn :: Literal -> Column a
+ Opaleye.Internal.PGTypes: strictDecodeUtf8 :: ByteString -> String
+ Opaleye.Internal.PGTypes: unsafePgFormatTime :: FormatTime t => Name -> String -> t -> Column c
+ Opaleye.Internal.PackMap: eitherFunction :: Functor f => (a -> f b) -> (a' -> f b') -> Either a a' -> f (Either b b')
+ Opaleye.Internal.PackMap: instance SumProfunctor (PackMap a b)
+ Opaleye.Internal.PackMap: overPM :: PackMap a b s t -> (a -> b) -> s -> t
+ Opaleye.Internal.PackMap: traversePM :: Applicative f => PackMap a b s t -> (a -> f b) -> s -> f t
+ Opaleye.Internal.Print: ppUpdateReturning :: Returning SqlUpdate -> Doc
+ Opaleye.Internal.Print: type TableAlias = String
+ Opaleye.Internal.QueryArr: runSimpleQueryArrStart :: QueryArr a b -> a -> (b, PrimQuery, Tag)
+ Opaleye.Internal.RunQuery: instance QueryRunnerColumnDefault PGJson String
+ Opaleye.Internal.RunQuery: instance QueryRunnerColumnDefault PGJsonb String
+ Opaleye.Internal.RunQuery: instance SumProfunctor QueryRunner
+ Opaleye.Internal.RunQuery: jsonFieldParser :: FieldParser String
+ Opaleye.Internal.RunQuery: jsonFieldTypeParser :: ByteString -> FieldParser String
+ Opaleye.Internal.RunQuery: jsonbFieldParser :: FieldParser String
+ Opaleye.Internal.Sql: SelectAttrs :: (NonEmpty (SqlExpr, Maybe SqlColumn)) -> SelectAttrs
+ Opaleye.Internal.Sql: Star :: SelectAttrs
+ Opaleye.Internal.Sql: data SelectAttrs
+ Opaleye.Internal.Sql: ensureColumns :: [(SqlExpr, Maybe a)] -> NonEmpty (SqlExpr, Maybe a)
+ Opaleye.Internal.Sql: instance Show SelectAttrs
+ Opaleye.Internal.Table: Zip :: NonEmpty [a] -> Zip a
+ Opaleye.Internal.Table: data Zip a
+ Opaleye.Internal.Table: instance Monoid (Zip a)
+ Opaleye.Internal.Table: newtype Writer columns dummy
+ Opaleye.Internal.Table: runWriter' :: Writer columns columns' -> NonEmpty columns -> (NonEmpty [PrimExpr], [String])
+ Opaleye.Internal.Table: unZip :: Zip a -> NonEmpty [a]
+ Opaleye.Internal.Tag: newtype Tag
+ Opaleye.Internal.Unpackspec: collectPEs :: Unpackspec s t -> s -> [PrimExpr]
+ Opaleye.Internal.Unpackspec: instance SumProfunctor Unpackspec
+ Opaleye.Internal.Values: newtype Valuesspec columns columns'
+ Opaleye.Manipulation: arrangeInsertMany :: Table columns a -> NonEmpty columns -> SqlInsert
+ Opaleye.Manipulation: arrangeInsertManySql :: Table columns a -> NonEmpty columns -> String
+ Opaleye.Manipulation: arrangeUpdateReturning :: Unpackspec returned ignored -> Table columnsW columnsR -> (columnsR -> columnsW) -> (columnsR -> Column PGBool) -> (columnsR -> returned) -> Returning SqlUpdate
+ Opaleye.Manipulation: arrangeUpdateReturningSql :: Unpackspec returned ignored -> Table columnsW columnsR -> (columnsR -> columnsW) -> (columnsR -> Column PGBool) -> (columnsR -> returned) -> String
+ Opaleye.Manipulation: runInsertMany :: Connection -> Table columns columns' -> [columns] -> IO Int64
+ Opaleye.Manipulation: runUpdateReturning :: Default QueryRunner returned haskells => Connection -> Table columnsW columnsR -> (columnsR -> columnsW) -> (columnsR -> Column PGBool) -> (columnsR -> returned) -> IO [haskells]
+ Opaleye.Manipulation: runUpdateReturningExplicit :: QueryRunner returned haskells -> Connection -> Table columnsW columnsR -> (columnsR -> columnsW) -> (columnsR -> Column PGBool) -> (columnsR -> returned) -> IO [haskells]
+ Opaleye.Operators: in_ :: (Functor f, Foldable f) => f (Column a) -> Column a -> Column PGBool
+ Opaleye.Operators: ors :: Foldable f => f (Column PGBool) -> Column PGBool
+ Opaleye.Order: ascNullsFirst :: PGOrd b => (a -> Column b) -> Order a
+ Opaleye.Order: class PGOrd a
+ Opaleye.Order: descNullsLast :: PGOrd b => (a -> Column b) -> Order a
+ Opaleye.Order: instance PGOrd PGBool
+ Opaleye.Order: instance PGOrd PGCitext
+ Opaleye.Order: instance PGOrd PGDate
+ Opaleye.Order: instance PGOrd PGFloat4
+ Opaleye.Order: instance PGOrd PGFloat8
+ Opaleye.Order: instance PGOrd PGInt2
+ Opaleye.Order: instance PGOrd PGInt4
+ Opaleye.Order: instance PGOrd PGInt8
+ Opaleye.Order: instance PGOrd PGNumeric
+ Opaleye.Order: instance PGOrd PGText
+ Opaleye.Order: instance PGOrd PGTime
+ Opaleye.Order: instance PGOrd PGTimestamp
+ Opaleye.Order: instance PGOrd PGTimestamptz
+ Opaleye.PGTypes: data PGJson
+ Opaleye.PGTypes: data PGJsonb
+ Opaleye.PGTypes: pgJSON :: String -> Column PGJson
+ Opaleye.PGTypes: pgJSONB :: String -> Column PGJsonb
+ Opaleye.PGTypes: pgLazyJSON :: ByteString -> Column PGJson
+ Opaleye.PGTypes: pgLazyJSONB :: ByteString -> Column PGJsonb
+ Opaleye.PGTypes: pgStrictJSON :: ByteString -> Column PGJson
+ Opaleye.PGTypes: pgStrictJSONB :: ByteString -> Column PGJsonb
+ Opaleye.RunQuery: class QueryRunnerColumnDefault pgType haskellType
+ Opaleye.RunQuery: queryRunnerColumnDefault :: QueryRunnerColumnDefault pgType haskellType => QueryRunnerColumn pgType haskellType
- Opaleye.Aggregate: max :: Aggregator (Column a) (Column a)
+ Opaleye.Aggregate: max :: PGOrd a => Aggregator (Column a) (Column a)
- Opaleye.Aggregate: min :: Aggregator (Column a) (Column a)
+ Opaleye.Aggregate: min :: PGOrd a => Aggregator (Column a) (Column a)
- Opaleye.Internal.HaskellDB.PrimQuery: OpAsc :: OrderOp
+ Opaleye.Internal.HaskellDB.PrimQuery: OpAsc :: OrderDirection
- Opaleye.Internal.HaskellDB.PrimQuery: OpDesc :: OrderOp
+ Opaleye.Internal.HaskellDB.PrimQuery: OpDesc :: OrderDirection
- Opaleye.Internal.HaskellDB.Sql: SqlAsc :: SqlOrder
+ Opaleye.Internal.HaskellDB.Sql: SqlAsc :: SqlOrderDirection
- Opaleye.Internal.HaskellDB.Sql: SqlDesc :: SqlOrder
+ Opaleye.Internal.HaskellDB.Sql: SqlDesc :: SqlOrderDirection
- Opaleye.Internal.HaskellDB.Sql: SqlInsert :: SqlTable -> [SqlColumn] -> [SqlExpr] -> SqlInsert
+ Opaleye.Internal.HaskellDB.Sql: SqlInsert :: SqlTable -> [SqlColumn] -> (NonEmpty [SqlExpr]) -> SqlInsert
- Opaleye.Internal.HaskellDB.Sql.Default: defaultSqlInsert :: SqlGenerator -> TableName -> Assoc -> SqlInsert
+ Opaleye.Internal.HaskellDB.Sql.Default: defaultSqlInsert :: SqlGenerator -> TableName -> [Attribute] -> NonEmpty [PrimExpr] -> SqlInsert
- Opaleye.Internal.HaskellDB.Sql.Generate: SqlGenerator :: (TableName -> [PrimExpr] -> Assoc -> SqlUpdate) -> (TableName -> [PrimExpr] -> SqlDelete) -> (TableName -> Assoc -> SqlInsert) -> (PrimExpr -> SqlExpr) -> (Literal -> String) -> (String -> String) -> SqlGenerator
+ Opaleye.Internal.HaskellDB.Sql.Generate: SqlGenerator :: (TableName -> [PrimExpr] -> Assoc -> SqlUpdate) -> (TableName -> [PrimExpr] -> SqlDelete) -> (TableName -> [Attribute] -> NonEmpty [PrimExpr] -> SqlInsert) -> (PrimExpr -> SqlExpr) -> (Literal -> String) -> (String -> String) -> SqlGenerator
- Opaleye.Internal.HaskellDB.Sql.Generate: sqlInsert :: SqlGenerator -> TableName -> Assoc -> SqlInsert
+ Opaleye.Internal.HaskellDB.Sql.Generate: sqlInsert :: SqlGenerator -> TableName -> [Attribute] -> NonEmpty [PrimExpr] -> SqlInsert
- Opaleye.Internal.Order: Order :: [SingleOrder a] -> Order a
+ Opaleye.Internal.Order: Order :: (a -> [(OrderOp, PrimExpr)]) -> Order a
- Opaleye.Internal.PackMap: extractAttr :: String -> Tag -> a -> PM [(Symbol, a)] PrimExpr
+ Opaleye.Internal.PackMap: extractAttr :: String -> Tag -> primExpr -> PM [(Symbol, primExpr)] PrimExpr
- Opaleye.Internal.PackMap: extractAttrPE :: (a -> String -> String) -> Tag -> a -> PM [(Symbol, a)] PrimExpr
+ Opaleye.Internal.PackMap: extractAttrPE :: (primExpr -> String -> String) -> Tag -> primExpr -> PM [(Symbol, primExpr)] PrimExpr
- Opaleye.Internal.PrimQuery: Join :: JoinType -> [(Symbol, PrimExpr)] -> PrimExpr -> PrimQuery -> PrimQuery -> PrimQuery
+ Opaleye.Internal.PrimQuery: Join :: JoinType -> PrimExpr -> PrimQuery -> PrimQuery -> PrimQuery
- Opaleye.Internal.PrimQuery: type PrimQueryFold p = (p, String -> [(Symbol, PrimExpr)] -> p, NonEmpty p -> [PrimExpr] -> p, [(Symbol, (Maybe AggrOp, PrimExpr))] -> p -> p, [OrderExpr] -> p -> p, LimitOp -> p -> p, JoinType -> [(Symbol, PrimExpr)] -> PrimExpr -> p -> p -> p, [Symbol] -> [[PrimExpr]] -> p, BinOp -> [(Symbol, (PrimExpr, PrimExpr))] -> (p, p) -> p)
+ Opaleye.Internal.PrimQuery: type PrimQueryFold p = (p, String -> [(Symbol, PrimExpr)] -> p, NonEmpty p -> [PrimExpr] -> p, [(Symbol, (Maybe AggrOp, PrimExpr))] -> p -> p, [OrderExpr] -> p -> p, LimitOp -> p -> p, JoinType -> PrimExpr -> p -> p -> p, [Symbol] -> [[PrimExpr]] -> p, BinOp -> [(Symbol, (PrimExpr, PrimExpr))] -> (p, p) -> p)
- Opaleye.Internal.Print: ppAttrs :: [(SqlExpr, Maybe SqlColumn)] -> Doc
+ Opaleye.Internal.Print: ppAttrs :: SelectAttrs -> Doc
- Opaleye.Internal.Print: ppGroupBy :: [SqlExpr] -> Doc
+ Opaleye.Internal.Print: ppGroupBy :: Maybe (NonEmpty SqlExpr) -> Doc
- Opaleye.Internal.Print: ppTable :: (SqlTable, Select) -> Doc
+ Opaleye.Internal.Print: ppTable :: (TableAlias, Select) -> Doc
- Opaleye.Internal.Print: tableAlias :: Int -> Select -> (SqlTable, Select)
+ Opaleye.Internal.Print: tableAlias :: Int -> Select -> (TableAlias, Select)
- Opaleye.Internal.RunQuery: QueryRunner :: (Unpackspec columns ()) -> (RowParser haskells) -> QueryRunner columns haskells
+ Opaleye.Internal.RunQuery: QueryRunner :: (Unpackspec columns ()) -> (columns -> RowParser haskells) -> (columns -> Bool) -> QueryRunner columns haskells
- Opaleye.Internal.RunQuery: QueryRunnerColumn :: (Unpackspec (Column coltype) ()) -> (FieldParser haskell) -> QueryRunnerColumn coltype haskell
+ Opaleye.Internal.RunQuery: QueryRunnerColumn :: (Unpackspec (Column pgType) ()) -> (FieldParser haskellType) -> QueryRunnerColumn pgType haskellType
- Opaleye.Internal.RunQuery: class QueryRunnerColumnDefault a b
+ Opaleye.Internal.RunQuery: class QueryRunnerColumnDefault pgType haskellType
- Opaleye.Internal.RunQuery: data QueryRunnerColumn coltype haskell
+ Opaleye.Internal.RunQuery: data QueryRunnerColumn pgType haskellType
- Opaleye.Internal.RunQuery: queryRunnerColumnDefault :: QueryRunnerColumnDefault a b => QueryRunnerColumn a b
+ Opaleye.Internal.RunQuery: queryRunnerColumnDefault :: QueryRunnerColumnDefault pgType haskellType => QueryRunnerColumn pgType haskellType
- Opaleye.Internal.Sql: From :: [(SqlExpr, Maybe SqlColumn)] -> [Select] -> [SqlExpr] -> [SqlExpr] -> [(SqlExpr, SqlOrder)] -> Maybe Int -> Maybe Int -> From
+ Opaleye.Internal.Sql: From :: SelectAttrs -> [Select] -> [SqlExpr] -> Maybe (NonEmpty SqlExpr) -> [(SqlExpr, SqlOrder)] -> Maybe Int -> Maybe Int -> From
- Opaleye.Internal.Sql: Join :: JoinType -> [(SqlExpr, Maybe SqlColumn)] -> (Select, Select) -> SqlExpr -> Join
+ Opaleye.Internal.Sql: Join :: JoinType -> (Select, Select) -> SqlExpr -> Join
- Opaleye.Internal.Sql: Values :: [(SqlExpr, Maybe SqlColumn)] -> [[SqlExpr]] -> Values
+ Opaleye.Internal.Sql: Values :: SelectAttrs -> [[SqlExpr]] -> Values
- Opaleye.Internal.Sql: attrs :: From -> [(SqlExpr, Maybe SqlColumn)]
+ Opaleye.Internal.Sql: attrs :: From -> SelectAttrs
- Opaleye.Internal.Sql: groupBy :: From -> [SqlExpr]
+ Opaleye.Internal.Sql: groupBy :: From -> Maybe (NonEmpty SqlExpr)
- Opaleye.Internal.Sql: join :: JoinType -> [(Symbol, PrimExpr)] -> PrimExpr -> Select -> Select -> Select
+ Opaleye.Internal.Sql: join :: JoinType -> PrimExpr -> Select -> Select -> Select
- Opaleye.Internal.Sql: vAttrs :: Values -> [(SqlExpr, Maybe SqlColumn)]
+ Opaleye.Internal.Sql: vAttrs :: Values -> SelectAttrs
- Opaleye.Internal.Table: Writer :: (PackMap (PrimExpr, String) () columns ()) -> Writer columns dummy
+ Opaleye.Internal.Table: Writer :: (forall f. Functor f => PackMap (f PrimExpr, String) () (f columns) ()) -> Writer columns dummy
- Opaleye.Manipulation: arrangeInsertReturning :: Unpackspec returned returned -> Table columnsW columnsR -> columnsW -> (columnsR -> returned) -> Returning SqlInsert
+ Opaleye.Manipulation: arrangeInsertReturning :: Unpackspec returned ignored -> Table columnsW columnsR -> columnsW -> (columnsR -> returned) -> Returning SqlInsert
- Opaleye.Manipulation: arrangeInsertReturningSql :: Unpackspec returned returned -> Table columnsW columnsR -> columnsW -> (columnsR -> returned) -> String
+ Opaleye.Manipulation: arrangeInsertReturningSql :: Unpackspec returned ignored -> Table columnsW columnsR -> columnsW -> (columnsR -> returned) -> String
- Opaleye.Manipulation: runInsertReturning :: (Default QueryRunner returned haskells, Default Unpackspec returned returned) => Connection -> Table columnsW columnsR -> columnsW -> (columnsR -> returned) -> IO [haskells]
+ Opaleye.Manipulation: runInsertReturning :: Default QueryRunner returned haskells => Connection -> Table columnsW columnsR -> columnsW -> (columnsR -> returned) -> IO [haskells]
- Opaleye.Manipulation: runInsertReturningExplicit :: QueryRunner returned haskells -> Unpackspec returned returned -> Connection -> Table columnsW columnsR -> columnsW -> (columnsR -> returned) -> IO [haskells]
+ Opaleye.Manipulation: runInsertReturningExplicit :: QueryRunner returned haskells -> Connection -> Table columnsW columnsR -> columnsW -> (columnsR -> returned) -> IO [haskells]
- Opaleye.Operators: (.<) :: Column a -> Column a -> Column PGBool
+ Opaleye.Operators: (.<) :: PGOrd a => Column a -> Column a -> Column PGBool
- Opaleye.Operators: (.<=) :: Column a -> Column a -> Column PGBool
+ Opaleye.Operators: (.<=) :: PGOrd a => Column a -> Column a -> Column PGBool
- Opaleye.Operators: (.>) :: Column a -> Column a -> Column PGBool
+ Opaleye.Operators: (.>) :: PGOrd a => Column a -> Column a -> Column PGBool
- Opaleye.Operators: (.>=) :: Column a -> Column a -> Column PGBool
+ Opaleye.Operators: (.>=) :: PGOrd a => Column a -> Column a -> Column PGBool
- Opaleye.Order: asc :: (a -> Column b) -> Order a
+ Opaleye.Order: asc :: PGOrd b => (a -> Column b) -> Order a
- Opaleye.Order: desc :: (a -> Column b) -> Order a
+ Opaleye.Order: desc :: PGOrd b => (a -> Column b) -> Order a
- Opaleye.RunQuery: data QueryRunnerColumn coltype haskell
+ Opaleye.RunQuery: data QueryRunnerColumn pgType haskellType

Files

CHANGELOG.md view
@@ -1,3 +1,14 @@+* Added `runUpdateReturning`+* Ordering operators and `max` and `min` aggregators are now restricted to a typeclass+* Added `stringAgg` and `arrayAgg` aggregations.+* Added `PGOrd` typeclass for typesafe ordering operations.+* Support sorting NULLs first or last with `ascNullsFirst` and `descNullsFirst`+* Added JSON types+* Added `runInsertMany`++Thanks to Travis Staton, Jakub Ryška and Christopher Lewis for helping+with these changes.+ ## 0.3.1.2  * Use time >= 1.4 and time-locale-compat@@ -15,8 +26,13 @@  ## 0.3 -* Replace `Default QueryRunner` with a new class `DefaultQueryRunnerColumn`, migrate with `s/Default QueryRunner/DefaultQueryRunnerColumn` and `s/def/queryRunnerColumnDefault/`-* Remove `ShowConstant`, use the monomorphic functions defined in the new module `Opaleye.PGTypes` instead. You will need to replace `Column Bool` with `Column PGBool` et.c. in query signatures+* Replace `Default QueryRunner` with a new class+  `DefaultQueryRunnerColumn`, migrate with `s/Default+  QueryRunner/DefaultQueryRunnerColumn` and+  `s/def/queryRunnerColumnDefault/`+* Remove `ShowConstant`, use the monomorphic functions defined in the+  new module `Opaleye.PGTypes` instead. You will need to replace+  `Column Bool` with `Column PGBool` etc. in query signatures * Re-export more modules from `Opaleye` * Add `boolAnd`, `boolOr,` `max`, and `min` aggregators * Add `lower` and `upper`
Doc/Tutorial/TutorialAdvanced.lhs view
@@ -47,7 +47,7 @@ > rangeOfChildrensAges = aggregate (p2 (A.groupBy, range)) (queryTable personTable)  -TutorialAdvanced> printSql rangeOfChildrensAges +TutorialAdvanced> printSql rangeOfChildrensAges SELECT result0_2 as result1,        (result1_2) - (result2_2) as result2 FROM (SELECT *
Doc/Tutorial/TutorialManipulation.lhs view
@@ -10,6 +10,7 @@ > > import           Data.Profunctor.Product (p3) > import           Data.Profunctor.Product.Default (Default, def)+> import qualified Opaleye.Internal.Unpackspec as U   Manipulation@@ -93,8 +94,11 @@ Opaleye supports it also.  > insertReturning :: String-> insertReturning = arrangeInsertReturningSql def table (Nothing, 4, 5)+> insertReturning = arrangeInsertReturningSql def' table (Nothing, 4, 5) >                                             (\(id_, _, _) -> id_)+>                   -- TODO: vv This is too messy+>                   where def' :: U.Unpackspec (Column a) (Column a)+>                         def' = def  ghci> putStrLn insertReturning INSERT INTO tablename (x,
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2014, Purely Agile Limited+Copyright (c) 2014-2015 Purely Agile Limited  All rights reserved. 
README.md view
@@ -1,4 +1,4 @@-# Brief introduction to Opaleye+# Brief introduction to Opaleye [![Hackage version](https://img.shields.io/hackage/v/opaleye.svg?style=flat)](https://hackage.haskell.org/package/opaleye) [![Build Status](https://img.shields.io/travis/tomjaguarpaw/haskell-opaleye.svg?style=flat)](https://travis-ci.org/tomjaguarpaw/haskell-opaleye)  Opaleye is a Haskell library which provides an SQL-generating embedded domain specific language for targeting Postgres.  You need Opaleye if@@ -8,7 +8,7 @@ > "Opaleye really is great. You've managed to bring what is so wonderful about relational databases and give it type safety and composition (i.e. what is wonderful about Haskell)" &ndash; Daniel-Patterson, [Position Development](http://www.positiondev.com/)+Patterson, [Position Development](http://positiondev.com/)  Opaleye allows you to define your database tables and write queries against them in Haskell code, and aims to be typesafe in the sense@@ -60,6 +60,16 @@ Commercial support for Opaleye is provided by [Purely Agile](http://www.purelyagile.com/). +# Backup maintainers++In the event of the main developer becoming unreachable, please+contact the following who are authorised to make bugfixes and+dependency version bumps:++* Adam Bergmark+* Erik Hesselink+* Oliver Charles+ # Contributors  The Opaleye Project was founded by Tom Ellis, inspired by theoretical@@ -69,9 +79,15 @@ Bjorn Bringert, Anders Hockersten, Torbjorn Martin, Jeremy Shaw and Justin Bailey. -Silk (Erik Hesselink, Adam Bergmark), Karamaan (Christopher Lewis),-Fynder (Renzo Carbonara, Oliver Charles) and Daniel Patterson-contributed code to the project.+The following individuals and organisations have made helpful+contributions:++* Silk (Erik Hesselink, Adam Bergmark)+* Karamaan (Christopher Lewis)+* Fynder (Renzo Carbonara, Oliver Charles)+* Daniel Patterson+* Jakub Ryška+* Travis Staton  Joseph Abrahamson, Alfredo Di Napoli and Mietek Bak performed useful reviews of early versions which helped improve the codebase.
+ Test/QuickCheck.hs view
@@ -0,0 +1,306 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE Rank2Types #-}++module QuickCheck where++import qualified Opaleye as O+import qualified Database.PostgreSQL.Simple as PGS+import qualified Test.QuickCheck as TQ+import           Control.Applicative (Applicative, pure, (<$>), (<*>), liftA2)+import qualified Data.Profunctor.Product.Default as D+import           Data.List (sort, sortBy)+import qualified Data.Profunctor.Product as PP+import qualified Data.Functor.Contravariant.Divisible as Divisible+import qualified Data.Monoid as Monoid+import qualified Data.Ord as Ord+import qualified Data.Set as Set+import qualified Data.Maybe as Maybe+import qualified Control.Arrow as Arrow++twoIntTable :: String+            -> O.Table (O.Column O.PGInt4, O.Column O.PGInt4)+                       (O.Column O.PGInt4, O.Column O.PGInt4)+twoIntTable n = O.Table n (PP.p2 (O.required "column1", O.required "column2"))++table1 :: O.Table (O.Column O.PGInt4, O.Column O.PGInt4)+                  (O.Column O.PGInt4, O.Column O.PGInt4)+table1 = twoIntTable "table1"++data QueryDenotation a =+  QueryDenotation { unQueryDenotation :: PGS.Connection -> IO [a] }++onList :: ([a] -> [b]) -> QueryDenotation a -> QueryDenotation b+onList f = QueryDenotation . (fmap . fmap) f . unQueryDenotation++type Columns = [Either (O.Column O.PGInt4) (O.Column O.PGBool)]+type Haskells = [Either Int Bool]++newtype ArbitraryQuery   = ArbitraryQuery (O.Query Columns)+newtype ArbitraryColumns = ArbitraryColumns { unArbitraryColumns :: Columns }+                        deriving Show+newtype ArbitraryPositiveInt = ArbitraryPositiveInt Int+                            deriving Show+newtype ArbitraryOrder = ArbitraryOrder { unArbitraryOrder :: [(Order, Int)] }+                      deriving Show+newtype ArbitraryGarble =+  ArbitraryGarble { unArbitraryGarble :: forall a. [a] -> [a] }++data Order = Asc | Desc deriving Show++unpackColumns :: O.Unpackspec Columns Columns+unpackColumns = eitherPP++instance Show ArbitraryQuery where+  show (ArbitraryQuery q) = O.showSqlForPostgresExplicit unpackColumns q++instance Show ArbitraryGarble where+  show = const "A permutation"++instance TQ.Arbitrary ArbitraryQuery where+  arbitrary = TQ.oneof [+      (ArbitraryQuery . pure . unArbitraryColumns)+        <$> TQ.arbitrary+    , return (ArbitraryQuery (fmap (\(x,y) -> [Left x, Left y]) (O.queryTable table1)))+    , do+        ArbitraryQuery q <- TQ.arbitrary+        aq (O.distinctExplicit eitherPP q)+    , do+        ArbitraryQuery q <- TQ.arbitrary+        l                <- TQ.choose (0, 100)+        aq (O.limit l q)+    , do+        ArbitraryQuery q <- TQ.arbitrary+        l                <- TQ.choose (0, 100)+        aq (O.offset l q)+    , do+        ArbitraryQuery q <- TQ.arbitrary+        o                <- TQ.arbitrary+        aq (O.orderBy (arbitraryOrder o) q)++    , do+        ArbitraryQuery q <- TQ.arbitrary+        f                <- TQ.arbitrary+        aq (fmap (unArbitraryGarble f) q)++    , do+        ArbitraryQuery q <- TQ.arbitrary+        aq (restrictFirstBool Arrow.<<< q)+    ]+    where aq = return . ArbitraryQuery+++instance TQ.Arbitrary ArbitraryColumns where+    arbitrary = do+    l <- TQ.listOf (TQ.oneof (map (return . Left) [-1, 0, 1]+                             ++ map (return . Right) [O.pgBool False, O.pgBool True]))+    return (ArbitraryColumns l)++instance TQ.Arbitrary ArbitraryPositiveInt where+  arbitrary = fmap ArbitraryPositiveInt (TQ.choose (0, 100))++instance TQ.Arbitrary ArbitraryOrder where+  arbitrary = fmap ArbitraryOrder+                   (TQ.listOf ((,)+                               <$> TQ.oneof [return Asc, return Desc]+                               <*> TQ.choose (0, 100)))++odds :: [a] -> [a]+odds []     = []+odds (x:xs) = x : evens xs++evens :: [a] -> [a]+evens []     = []+evens (_:xs) = odds xs++instance TQ.Arbitrary ArbitraryGarble where+  arbitrary = do+    i <- TQ.choose (0 :: Int, 4)++    return (ArbitraryGarble (\xs ->+        if i == 0 then+          evens xs ++ odds xs+        else if i == 1 then+          evens xs ++ evens xs+        else if i == 2 then+          odds xs ++ odds xs+        else if i == 3 then+          evens xs+        else+          odds xs))++arbitraryOrder :: ArbitraryOrder -> O.Order Columns+arbitraryOrder = Monoid.mconcat+                 . map (\(direction, index) ->+                         (case direction of+                             Asc  -> (\f -> Divisible.choose  f (O.asc id) (O.asc id))+                             Desc -> (\f -> Divisible.choose  f (O.desc id) (O.desc id)))+                         -- If the list is empty we have to conjure up+                         -- an arbitrary value of type Column+                         (\l -> let len = length l+                                in if len > 0 then+                                     l !! (index `mod` length l)+                                   else+                                     Left 0))+                 . unArbitraryOrder++arbitraryOrdering :: ArbitraryOrder -> Haskells -> Haskells -> Ord.Ordering+arbitraryOrdering = Monoid.mconcat+                    . map (\(direction, index) ->+                            (case direction of+                                Asc  -> id+                                Desc -> flip)+                         -- If the list is empty we have to conjure up+                         -- an arbitrary value of type Column+                         --+                         -- Note that this one will compare Left Int+                         -- to Right Bool, but it never gets asked to+                         -- do so, so we don't care.+                            (Ord.comparing (\l -> let len = length l+                                                  in if len > 0 then+                                                        l !! (index `mod` length l)+                                                     else+                                                        Left 0)))+                    . unArbitraryOrder++instance Functor QueryDenotation where+  fmap f = QueryDenotation . (fmap . fmap . fmap) f .unQueryDenotation++instance Applicative QueryDenotation where+  pure    = QueryDenotation . pure . pure . pure+  f <*> x = QueryDenotation ((liftA2 . liftA2 . liftA2) ($)+                                (unQueryDenotation f) (unQueryDenotation x))++denotation :: O.QueryRunner columns a -> O.Query columns -> QueryDenotation a+denotation qr q = QueryDenotation (\conn -> O.runQueryExplicit qr conn q)++denotation' :: O.Query Columns -> QueryDenotation Haskells+denotation' = denotation eitherPP++denotation2 :: O.Query (Columns, Columns)+            -> QueryDenotation (Haskells, Haskells)+denotation2 = denotation (eitherPP PP.***! eitherPP)++-- { Comparing the results++compareNoSort :: Eq a+              => PGS.Connection+              -> QueryDenotation a+              -> QueryDenotation a+              -> IO Bool+compareNoSort conn one two = do+  one' <- unQueryDenotation one conn+  two' <- unQueryDenotation two conn+  return (one' == two')++compare' :: Ord a+         => PGS.Connection+         -> QueryDenotation a+         -> QueryDenotation a+         -> IO Bool+compare' conn one two = do+  one' <- unQueryDenotation one conn+  two' <- unQueryDenotation two conn+  return (sort one' == sort two')++-- }++-- { The tests++fmap' :: PGS.Connection -> ArbitraryGarble -> ArbitraryQuery -> IO Bool+fmap' conn f (ArbitraryQuery q) = do+  compareNoSort conn (denotation' (fmap (unArbitraryGarble f) q))+                     (onList (fmap (unArbitraryGarble f)) (denotation' q))++apply :: PGS.Connection -> ArbitraryQuery -> ArbitraryQuery -> IO Bool+apply conn (ArbitraryQuery q1) (ArbitraryQuery q2) = do+  compare' conn (denotation2 ((,) <$> q1 <*> q2))+                ((,) <$> denotation' q1 <*> denotation' q2)++limit :: PGS.Connection -> ArbitraryPositiveInt -> ArbitraryQuery -> IO Bool+limit conn (ArbitraryPositiveInt l) (ArbitraryQuery q) = do+  compareNoSort conn (denotation' (O.limit l q))+                     (onList (take l) (denotation' q))++offset :: PGS.Connection -> ArbitraryPositiveInt -> ArbitraryQuery -> IO Bool+offset conn (ArbitraryPositiveInt l) (ArbitraryQuery q) = do+  compareNoSort conn (denotation' (O.offset l q))+                     (onList (drop l) (denotation' q))++order :: PGS.Connection -> ArbitraryOrder -> ArbitraryQuery -> IO Bool+order conn o (ArbitraryQuery q) = do+  compareNoSort conn (denotation' (O.orderBy (arbitraryOrder o) q))+                     (onList (sortBy (arbitraryOrdering o)) (denotation' q))++distinct :: PGS.Connection -> ArbitraryQuery -> IO Bool+distinct conn (ArbitraryQuery q) = do+  compare' conn (denotation' (O.distinctExplicit eitherPP q))+                (onList nub (denotation' q))++restrict :: PGS.Connection -> ArbitraryQuery -> IO Bool+restrict conn (ArbitraryQuery q) = do+  compareNoSort conn (denotation' (restrictFirstBool Arrow.<<< q))+                     (onList restrictFirstBoolList (denotation' q))++-- }++-- { Running the QuickCheck++run :: PGS.Connection -> IO ()+run conn = do+  let propFmap      = (fmap . fmap) TQ.ioProperty (fmap' conn)+      propApply     = (fmap . fmap) TQ.ioProperty (apply conn)+      propLimit     = (fmap . fmap) TQ.ioProperty (limit conn)+      propOffset    = (fmap . fmap) TQ.ioProperty (offset conn)+      propOrder     = (fmap . fmap) TQ.ioProperty (order conn)+      propDistinct  = fmap          TQ.ioProperty (distinct conn)+      propRestrict  = fmap          TQ.ioProperty (restrict conn)++  let t p = errorIfNotSuccess =<< TQ.quickCheckWithResult (TQ.stdArgs { TQ.maxSuccess = 1000 }) p++  t propFmap+  t propApply+  t propLimit+  t propOffset+  t propOrder+  t propDistinct+  t propRestrict++-- }++-- { Utilities++nub :: Ord a => [a] -> [a]+nub = Set.toList . Set.fromList++eitherPP :: (D.Default p a a', D.Default p b b',+             PP.SumProfunctor p, PP.ProductProfunctor p)+         => p [Either a b] [Either a' b']+eitherPP = PP.list (D.def PP.+++! D.def)++errorIfNotSuccess :: TQ.Result -> IO ()+errorIfNotSuccess r = case r of+  TQ.Success _ _ _ -> return ()+  _                -> error "Failed"++firstBoolOrTrue :: b -> [Either a b] -> (b, [Either a b])+firstBoolOrTrue true c = (b, c)+  where b = case Maybe.mapMaybe isBool c of+          []    -> true+          (x:_) -> x++isBool :: Either a b+       -> Maybe b+isBool (Left _)  = Nothing+isBool (Right l) = Just l++restrictFirstBool :: O.QueryArr Columns Columns+restrictFirstBool = Arrow.arr snd+      Arrow.<<< Arrow.first O.restrict+      Arrow.<<< Arrow.arr (firstBoolOrTrue (O.pgBool True))++restrictFirstBoolList :: [Haskells] -> [Haskells]+restrictFirstBoolList = map snd+                        . filter fst+                        . map (firstBoolOrTrue True)++-- }
Test/Test.hs view
@@ -3,6 +3,8 @@  module Main where +import qualified QuickCheck+ import           Opaleye (Column, Nullable, Query, QueryArr, (.==), (.>)) import qualified Opaleye as O @@ -16,6 +18,7 @@ import qualified Data.String as String  import qualified System.Exit as Exit+import qualified System.Environment as Environment  import qualified Control.Applicative as A import qualified Control.Arrow as Arr@@ -35,6 +38,13 @@                                , PGS.connectPassword = "tom"                                , PGS.connectDatabase = "opaleye_test" } +connectInfoTravis :: PGS.ConnectInfo+connectInfoTravis =  PGS.ConnectInfo { PGS.connectHost = "localhost"+                                     , PGS.connectPort = 5432+                                     , PGS.connectUser = "postgres"+                                     , PGS.connectPassword = ""+                                     , PGS.connectDatabase = "opaleye_test" }+ -- }  {-@@ -115,8 +125,9 @@ table1F :: O.Table (Column O.PGInt4, Column O.PGInt4) (Column O.PGInt4, Column O.PGInt4) table1F = fmap (\(col1, col2) -> (col1 + col2, col1 - col2)) table1 +-- This is implicitly testing our ability to handle upper case letters in table names. table2 :: O.Table (Column O.PGInt4, Column O.PGInt4) (Column O.PGInt4, Column O.PGInt4)-table2 = twoIntTable "table2"+table2 = twoIntTable "TABLE2"  table3 :: O.Table (Column O.PGInt4, Column O.PGInt4) (Column O.PGInt4, Column O.PGInt4) table3 = twoIntTable "table3"@@ -124,6 +135,13 @@ table4 :: O.Table (Column O.PGInt4, Column O.PGInt4) (Column O.PGInt4, Column O.PGInt4) table4 = twoIntTable "table4" +table5 :: O.Table (Maybe (Column O.PGInt4), Maybe (Column  O.PGInt4))+                  (Column O.PGInt4, Column O.PGInt4)+table5 = O.Table "table5" (PP.p2 (O.optional "column1", O.optional "column2"))++table6 :: O.Table (Column O.PGText, Column O.PGText) (Column O.PGText, Column O.PGText)+table6 = O.Table "table6" (PP.p2 (O.required "column1", O.required "column2"))+ tableKeywordColNames :: O.Table (Column O.PGInt4, Column O.PGInt4)                                 (Column O.PGInt4, Column O.PGInt4) tableKeywordColNames = O.Table "keywordtable" (PP.p2 (O.required "column", O.required "where"))@@ -137,6 +155,9 @@ table3Q :: Query (Column O.PGInt4, Column O.PGInt4) table3Q = O.queryTable table3 +table6Q :: Query (Column O.PGText, Column O.PGText)+table6Q = O.queryTable table6+ table1dataG :: Num a => [(a, a)] table1dataG = [ (1, 100)               , (1, 100)@@ -178,14 +199,38 @@ table4columndata :: [(Column O.PGInt4, Column O.PGInt4)] table4columndata = table4dataG -dropAndCreateTable :: (String, [String]) -> PGS.Query-dropAndCreateTable (t, cols) = String.fromString drop_-  where drop_ = "DROP TABLE IF EXISTS " ++ t ++ ";"-                ++ "CREATE TABLE " ++ t+table6data :: [(String, String)]+table6data = [("xy", "a"), ("z", "a"), ("more text", "a")]++table6columndata :: [(Column O.PGText, Column O.PGText)]+table6columndata = map (\(column1, column2) -> (O.pgString column1, O.pgString column2)) table6data++-- We have to quote the table names here because upper case letters in+-- table names are treated as lower case unless the name is quoted!+dropAndCreateTable :: String -> (String, [String]) -> PGS.Query+dropAndCreateTable columnType (t, cols) = String.fromString drop_+  where drop_ = "DROP TABLE IF EXISTS \"" ++ t ++ "\";"+                ++ "CREATE TABLE \"" ++ t ++ "\""                 ++ " (" ++ commas cols ++ ");"-        integer c = ("\"" ++ c ++ "\"" ++ " integer")+        integer c = ("\"" ++ c ++ "\"" ++ " " ++ columnType)         commas = L.intercalate "," . map integer-        ++dropAndCreateTableInt :: (String, [String]) -> PGS.Query+dropAndCreateTableInt = dropAndCreateTable "integer"++dropAndCreateTableText :: (String, [String]) -> PGS.Query+dropAndCreateTableText = dropAndCreateTable "text"++-- We have to quote the table names here because upper case letters in+-- table names are treated as lower case unless the name is quoted!+dropAndCreateTableSerial :: (String, [String]) -> PGS.Query+dropAndCreateTableSerial (t, cols) = String.fromString drop_+  where drop_ = "DROP TABLE IF EXISTS \"" ++ t ++ "\";"+                ++ "CREATE TABLE \"" ++ t ++ "\""+                ++ " (" ++ commas cols ++ ");"+        integer c = ("\"" ++ c ++ "\"" ++ " SERIAL")+        commas = L.intercalate "," . map integer+ type Table_ = (String, [String])  -- This should ideally be derived from the table definition above@@ -194,12 +239,20 @@  -- This should ideally be derived from the table definition above tables :: [Table_]-tables = map columns2 ["table1", "table2", "table3", "table4"]+tables = map columns2 ["table1", "TABLE2", "table3", "table4"]          ++ [("keywordtable", ["column", "where"])] +serialTables :: [Table_]+serialTables = map columns2 ["table5"]+ dropAndCreateDB :: PGS.Connection -> IO ()-dropAndCreateDB conn = mapM_ execute tables-  where execute = PGS.execute_ conn . dropAndCreateTable+dropAndCreateDB conn = do+  mapM_ execute tables+  executeTextTable+  mapM_ executeSerial serialTables+  where execute = PGS.execute_ conn . dropAndCreateTableInt+        executeTextTable = (PGS.execute_ conn . dropAndCreateTableText . columns2) "table6"+        executeSerial = PGS.execute_ conn . dropAndCreateTableSerial  type Test = PGS.Connection -> IO Bool @@ -266,14 +319,14 @@ testDistinct = testG (O.distinct table1Q)                (\r -> L.sort (L.nub table1data) == L.sort r) --- FIXME: the unsafeCoerce is currently needed because the type+-- FIXME: the unsafeCoerceColumn is currently needed because the type -- changes required for aggregation are not currently dealt with by -- Opaleye. aggregateCoerceFIXME :: QueryArr (Column O.PGInt4) (Column O.PGInt8) aggregateCoerceFIXME = Arr.arr aggregateCoerceFIXME'  aggregateCoerceFIXME' :: Column a -> Column O.PGInt8-aggregateCoerceFIXME' = O.unsafeCoerce+aggregateCoerceFIXME' = O.unsafeCoerceColumn  testAggregate :: Test testAggregate = testG (Arr.second aggregateCoerceFIXME@@ -289,6 +342,18 @@                            (\(x, y) -> aggregateCoerceFIXME' x * y)                            (PP.p2 (O.sum, O.count)) +testStringArrayAggregate :: Test+testStringArrayAggregate = testG q expected+  where q = O.aggregate (PP.p2 (O.arrayAgg, O.min)) table6Q+        expected r = [(map fst table6data, minimum (map snd table6data))] == r++testStringAggregate :: Test+testStringAggregate = testG q expected+  where q = O.aggregate (PP.p2 ((O.stringAgg . O.pgString) "_", O.groupBy)) table6Q+        expected r = [(+          (foldl1 (\x y -> x ++ "_" ++ y) . map fst) table6data ,+          head (map snd table6data))] == r+ testOrderByG :: O.Order (Column O.PGInt4, Column O.PGInt4)                 -> ((Int, Int) -> (Int, Int) -> Ordering)                 -> Test@@ -450,6 +515,7 @@       then return False       else do       returned <- O.runInsertReturning conn table4 insertT returning+      _ <- O.runInsertMany conn table4 insertTMany       resultI <- runQueryTable4        return ((resultI == expectedI) && (returned == expectedR))@@ -467,8 +533,11 @@         insertT :: (Column O.PGInt4, Column O.PGInt4)         insertT = (1, 2) +        insertTMany :: [(Column O.PGInt4, Column O.PGInt4)]+        insertTMany = [(20, 30), (40, 50)]+         expectedI :: [(Int, Int)]-        expectedI = [(1, 10), (1, 2)]+        expectedI = [(1, 10), (1, 2), (20, 30), (40, 50)]         returning (x, y) = x - y         expectedR :: [Int]         expectedR = [-1]@@ -480,24 +549,59 @@   _ <- q   return True -  +testInsertSerial :: Test+testInsertSerial conn = do+  _ <- O.runInsert conn table5 (Just 10, Just 20)+  _ <- O.runInsert conn table5 (Just 30, Nothing)+  _ <- O.runInsert conn table5 (Nothing, Nothing)+  _ <- O.runInsert conn table5 (Nothing, Just 40) +  resultI <- O.runQuery conn (O.queryTable table5)++  return (resultI == expected)++  where expected :: [(Int, Int)]+        expected = [ (10, 20)+                   , (30, 1)+                   , (1, 2)+                   , (2, 40) ]+ allTests :: [Test] allTests = [testSelect, testProduct, testRestrict, testNum, testDiv, testCase,-            testDistinct, testAggregate, testAggregateProfunctor,+            testDistinct, testAggregate, testAggregateProfunctor, testStringAggregate,             testOrderBy, testOrderBy2, testOrderBySame, testLimit, testOffset,             testLimitOffset, testOffsetLimit, testDistinctAndAggregate,             testDoubleDistinct, testDoubleAggregate, testDoubleLeftJoin,             testDoubleValues, testDoubleUnionAll,             testLeftJoin, testLeftJoinNullable, testThreeWayProduct, testValues,             testValuesEmpty, testUnionAll, testTableFunctor, testUpdate,-            testKeywordColNames+            testKeywordColNames, testInsertSerial            ] +-- Environment.getEnv throws an exception on missing environment variable!+getEnv :: String -> IO (Maybe String)+getEnv var = do+  environment <- Environment.getEnvironment+  return (lookup var environment)++-- Using an envvar is unpleasant, but it will do for now.+travis :: IO Bool+travis = do+    travis' <- getEnv "TRAVIS"++    return (case travis' of+               Nothing    -> False+               Just "yes" -> True+               Just _     -> False)+ main :: IO () main = do-  conn <- PGS.connect connectInfo+  travis' <- travis +  let connectInfo' = if travis' then connectInfoTravis else connectInfo++  conn <- PGS.connect connectInfo'+   dropAndCreateDB conn    let insert (writeable, columndata) =@@ -507,6 +611,10 @@                , (table2, table2columndata)                , (table3, table3columndata)                , (table4, table4columndata) ]+  insert (table6, table6columndata)++  -- Need to run quickcheck after table data has been inserted+  QuickCheck.run conn    results <- mapM ($ conn) allTests 
opaleye.cabal view
@@ -1,12 +1,14 @@ name:            opaleye-version:         0.3.1.2+copyright:       Copyright (c) 2014-2015 Purely Agile Limited+version:         0.4.0.0 synopsis:        An SQL-generating DSL targeting PostgreSQL description:     An SQL-generating DSL targeting PostgreSQL.  Allows                  Postgres queries to be written within Haskell in a                  typesafe and composable fashion. homepage:        https://github.com/tomjaguarpaw/haskell-opaleye+bug-reports:     https://github.com/tomjaguarpaw/haskell-opaleye/issues license:         BSD3-license-File:    LICENSE+license-file:    LICENSE author:          Purely Agile maintainer:      Purely Agile category:        Database@@ -14,32 +16,34 @@ cabal-version:   >= 1.8 extra-doc-files: *.md,                  Doc/*.md+tested-with:     GHC==7.10.1, GHC==7.8.4, GHC==7.6.3  source-repository head-  Type:     git-  Location: https://github.com/tomjaguarpaw/haskell-opaleye+  type:     git+  location: https://github.com/tomjaguarpaw/haskell-opaleye.git  library   hs-source-dirs: src   build-depends:       -- attoparsec can be removed once postgresql-simple patch in       -- Internal.RunQuery is merged upstream-      attoparsec          >= 0.10.3  && < 0.13+      attoparsec          >= 0.10.3  && < 0.14     , base                >= 4       && < 5     , base16-bytestring   >= 0.1.1.6 && < 0.2     , case-insensitive    >= 1.2     && < 1.3     , bytestring          >= 0.10    && < 0.11-    , contravariant       >= 0.4.4   && < 1.4+    , contravariant       >= 1.2     && < 1.4     , postgresql-simple   >= 0.4.8.0 && < 0.5     , pretty              >= 1.1.1.0 && < 1.2-    , product-profunctors >= 0.5     && < 0.7-    , profunctors         >= 4.0     && < 4.5+    , product-profunctors >= 0.6.2   && < 0.7+    , profunctors         >= 4.0     && < 5.2     , semigroups          >= 0.13    && < 0.17     , text                >= 0.11    && < 1.3     , transformers        >= 0.3     && < 0.5     , time                >= 1.4     && < 1.6     , time-locale-compat  >= 0.1     && < 0.2     , uuid                >= 1.3     && < 1.4+    , void                >= 0.4     && < 0.8   exposed-modules: Opaleye,                    Opaleye.Aggregate,                    Opaleye.Binary,@@ -64,6 +68,7 @@                    Opaleye.Internal.Order,                    Opaleye.Internal.Optimize,                    Opaleye.Internal.PackMap,+                   Opaleye.Internal.PGTypes,                    Opaleye.Internal.PrimQuery,                    Opaleye.Internal.Print,                    Opaleye.Internal.QueryArr,@@ -84,12 +89,17 @@ test-suite test   type: exitcode-stdio-1.0   main-is: Test.hs+  other-modules: QuickCheck   hs-source-dirs: Test   build-depends:     base >= 4 && < 5,+    containers,+    contravariant,     postgresql-simple,     profunctors,     product-profunctors,+    QuickCheck,+    semigroups,     opaleye   ghc-options: -Wall 
src/Opaleye/Aggregate.hs view
@@ -3,9 +3,11 @@  import qualified Opaleye.Internal.Aggregate as A import           Opaleye.Internal.Aggregate (Aggregator)+import qualified Opaleye.Internal.Column as IC import           Opaleye.QueryArr (Query) import qualified Opaleye.Internal.QueryArr as Q import qualified Opaleye.Column as C+import qualified Opaleye.Order as Ord import qualified Opaleye.PGTypes as T import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ @@ -39,11 +41,11 @@ avg = A.makeAggr HPQ.AggrAvg  -- | Maximum of a group-max :: Aggregator (C.Column a) (C.Column a)+max :: Ord.PGOrd a => Aggregator (C.Column a) (C.Column a) max = A.makeAggr HPQ.AggrMax  -- | Maximum of a group-min :: Aggregator (C.Column a) (C.Column a)+min :: Ord.PGOrd a => Aggregator (C.Column a) (C.Column a) min = A.makeAggr HPQ.AggrMin  boolOr :: Aggregator (C.Column T.PGBool) (C.Column T.PGBool)@@ -51,3 +53,9 @@  boolAnd :: Aggregator (C.Column T.PGBool) (C.Column T.PGBool) boolAnd = A.makeAggr HPQ.AggrBoolAnd++arrayAgg :: Aggregator (C.Column a) (C.Column (T.PGArray a))+arrayAgg = A.makeAggr HPQ.AggrArr++stringAgg :: C.Column T.PGText -> Aggregator (C.Column T.PGText) (C.Column T.PGText)+stringAgg = A.makeAggr' . Just . HPQ.AggrStringAggr . IC.unColumn
src/Opaleye/Column.hs view
@@ -1,9 +1,10 @@ module Opaleye.Column (module Opaleye.Column,                        Column,                        Nullable,-                       unsafeCoerce)  where+                       unsafeCoerce,+                       unsafeCoerceColumn)  where -import           Opaleye.Internal.Column (Column, Nullable, unsafeCoerce)+import           Opaleye.Internal.Column (Column, Nullable, unsafeCoerce, unsafeCoerceColumn) import qualified Opaleye.Internal.Column as C import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ import qualified Opaleye.PGTypes as T@@ -24,7 +25,7 @@ matchNullable :: Column b -> (Column a -> Column b) -> Column (Nullable a)               -> Column b matchNullable replacement f x = C.unsafeIfThenElse (isNull x) replacement-                                                   (f (unsafeCoerce x))+                                                   (f (unsafeCoerceColumn x))  -- | If the @Column (Nullable a)@ is NULL then return the provided -- @Column a@ otherwise return the underlying @Column a@.@@ -35,7 +36,7 @@  -- | The Opaleye equivalent of 'Data.Maybe.Just' toNullable :: Column a -> Column (Nullable a)-toNullable = unsafeCoerce+toNullable = unsafeCoerceColumn  -- | If the argument is 'Data.Maybe.Nothing' return NULL otherwise return the -- provided value coerced to a nullable type.
src/Opaleye/Internal/Aggregate.hs view
@@ -16,6 +16,11 @@ An 'Aggregator' takes a collection of rows of type @a@, groups them, and transforms each group into a single row of type @b@. This corresponds to aggregators using @GROUP BY@ in SQL.++An 'Aggregator' corresponds closely to a 'Control.Foldl.Fold' from the+@foldl@ package.  Whereas an 'Aggregator' @a@ @b@ takes each group of+type @a@ to a single row of type @b@, a 'Control.Foldl.Fold' @a@ @b@+takes a list of @a@ and returns a single row of type @b@. -} newtype Aggregator a b = Aggregator                          (PM.PackMap (Maybe HPQ.AggrOp, HPQ.PrimExpr) HPQ.PrimExpr@@ -30,7 +35,7 @@  runAggregator :: Applicative f => Aggregator a b               -> ((Maybe HPQ.AggrOp, HPQ.PrimExpr) -> f HPQ.PrimExpr) -> a -> f b-runAggregator (Aggregator a) = PM.packmap a+runAggregator (Aggregator a) = PM.traversePM a  aggregateU :: Aggregator a b            -> (a, PQ.PrimQuery, T.Tag) -> (b, PQ.PrimQuery, T.Tag)@@ -59,5 +64,8 @@ instance PP.ProductProfunctor Aggregator where   empty = PP.defaultEmpty   (***!) = PP.defaultProfunctorProduct++instance PP.SumProfunctor Aggregator where+  Aggregator x1 +++! Aggregator x2 = Aggregator (x1 PP.+++! x2)  -- }
src/Opaleye/Internal/Binary.hs view
@@ -21,14 +21,14 @@                              HPQ.PrimExpr extractBinaryFields = PM.extractAttr "binary" -data Binaryspec columns columns' =+newtype Binaryspec columns columns' =   Binaryspec (PM.PackMap (HPQ.PrimExpr, HPQ.PrimExpr) HPQ.PrimExpr                          (columns, columns) columns')  runBinaryspec :: Applicative f => Binaryspec columns columns'                  -> ((HPQ.PrimExpr, HPQ.PrimExpr) -> f HPQ.PrimExpr)                  -> (columns, columns) -> f columns'-runBinaryspec (Binaryspec b) = PM.packmap b+runBinaryspec (Binaryspec b) = PM.traversePM b  binaryspecColumn :: Binaryspec (Column a) (Column a) binaryspecColumn = Binaryspec (PM.PackMap (\f (Column e, Column e')
src/Opaleye/Internal/Column.hs view
@@ -12,8 +12,12 @@ unColumn :: Column a -> HPQ.PrimExpr unColumn (Column e) = e +{-# DEPRECATED unsafeCoerce "Use unsafeCoerceColumn instead" #-} unsafeCoerce :: Column a -> Column b-unsafeCoerce (Column e) = Column e+unsafeCoerce = unsafeCoerceColumn++unsafeCoerceColumn :: Column a -> Column b+unsafeCoerceColumn (Column e) = Column e  binOp :: HPQ.BinOp -> Column a -> Column b -> Column c binOp op (Column e) (Column e') = Column (HPQ.BinExpr op e e')
src/Opaleye/Internal/Distinct.hs view
@@ -20,7 +20,7 @@                  -> Query columns -> Query columns' distinctExplicit (Distinctspec agg) = aggregate agg -data Distinctspec a b = Distinctspec (Aggregator a b)+newtype Distinctspec a b = Distinctspec (Aggregator a b)  instance Default Distinctspec (Column a) (Column a) where   def = Distinctspec groupBy@@ -40,5 +40,8 @@ instance PP.ProductProfunctor Distinctspec where   empty = PP.defaultEmpty   (***!) = PP.defaultProfunctorProduct++instance PP.SumProfunctor Distinctspec where+  Distinctspec x1 +++! Distinctspec x2 = Distinctspec (x1 PP.+++! x2)  -- }
src/Opaleye/Internal/HaskellDB/PrimQuery.hs view
@@ -21,26 +21,31 @@                 | UnExpr    UnOp PrimExpr                 | AggrExpr  AggrOp PrimExpr                 | ConstExpr Literal-		| CaseExpr [(PrimExpr,PrimExpr)] PrimExpr+                | CaseExpr [(PrimExpr,PrimExpr)] PrimExpr                 | ListExpr [PrimExpr]                 | ParamExpr (Maybe Name) PrimExpr                 | FunExpr Name [PrimExpr]                 | CastExpr Name PrimExpr -- ^ Cast an expression to a given type.+                | DefaultInsertExpr -- Indicate that we want to insert the+                                    -- default value into a column.+                                    -- TODO: I'm not sure this belongs+                                    -- here.  Perhaps a special type is+                                    -- needed for insert expressions.                 deriving (Read,Show)  data Literal = NullLit-	     | DefaultLit            -- ^ represents a default value-	     | BoolLit Bool-	     | StringLit String+             | DefaultLit            -- ^ represents a default value+             | BoolLit Bool+             | StringLit String              | ByteStringLit ByteString-	     | IntegerLit Integer-	     | DoubleLit Double-	     | OtherLit String       -- ^ used for hacking in custom SQL-	       deriving (Read,Show)+             | IntegerLit Integer+             | DoubleLit Double+             | OtherLit String       -- ^ used for hacking in custom SQL+               deriving (Read,Show) -data BinOp      = OpEq | OpLt | OpLtEq | OpGt | OpGtEq | OpNotEq +data BinOp      = OpEq | OpLt | OpLtEq | OpGt | OpGtEq | OpNotEq                 | OpAnd | OpOr-                | OpLike | OpIn +                | OpLike | OpIn                 | OpOther String                  | OpCat@@ -62,12 +67,19 @@  data AggrOp     = AggrCount | AggrSum | AggrAvg | AggrMin | AggrMax                 | AggrStdDev | AggrStdDevP | AggrVar | AggrVarP-                | AggrBoolOr | AggrBoolAnd+                | AggrBoolOr | AggrBoolAnd | AggrArr | AggrStringAggr PrimExpr                 | AggrOther String                 deriving (Show,Read) -data OrderExpr = OrderExpr OrderOp PrimExpr +data OrderExpr = OrderExpr OrderOp PrimExpr                deriving (Show) -data OrderOp = OpAsc | OpDesc+data OrderNulls = NullsFirst | NullsLast+                deriving Show++data OrderDirection = OpAsc | OpDesc+                    deriving Show++data OrderOp = OrderOp { orderDirection :: OrderDirection+                       , orderNulls     :: OrderNulls }                deriving (Show)
src/Opaleye/Internal/HaskellDB/Sql.hs view
@@ -2,32 +2,30 @@ --                HWT Group (c) 2003, haskelldb-users@lists.sourceforge.net -- License     :  BSD-style -module Opaleye.Internal.HaskellDB.Sql ( -                               SqlTable,-                               SqlColumn(..),-                               SqlName,-                               SqlOrder(..),+module Opaleye.Internal.HaskellDB.Sql where -	                       SqlUpdate(..), -	                       SqlDelete(..), -	                       SqlInsert(..),  -                               SqlExpr(..),-	                      ) where-+import qualified Data.List.NonEmpty as NEL  ----------------------------------------------------------- -- * SQL data type ----------------------------------------------------------- -type SqlTable = String+newtype SqlTable = SqlTable String deriving Show  newtype SqlColumn = SqlColumn String deriving Show  -- | A valid SQL name for a parameter. type SqlName = String -data SqlOrder = SqlAsc | SqlDesc+data SqlOrderNulls = SqlNullsFirst | SqlNullsLast+                   deriving Show++data SqlOrderDirection = SqlAsc | SqlDesc+                       deriving Show++data SqlOrder = SqlOrder { sqlOrderDirection :: SqlOrderDirection+                         , sqlOrderNulls     :: SqlOrderNulls }   deriving Show  -- | Expressions in SQL statements.@@ -38,12 +36,13 @@              | FunSqlExpr     String [SqlExpr]              | AggrFunSqlExpr String [SqlExpr] -- ^ Aggregate functions separate from normal functions.              | ConstSqlExpr   String-	     | CaseSqlExpr    [(SqlExpr,SqlExpr)] SqlExpr+             | CaseSqlExpr    [(SqlExpr,SqlExpr)] SqlExpr              | ListSqlExpr    [SqlExpr]              | ParamSqlExpr (Maybe SqlName) SqlExpr              | PlaceHolderSqlExpr              | ParensSqlExpr SqlExpr-             | CastSqlExpr String SqlExpr +             | CastSqlExpr String SqlExpr+             | DefaultSqlExpr   deriving Show  -- | Data type for SQL UPDATE statements.@@ -53,4 +52,4 @@ data SqlDelete  = SqlDelete SqlTable [SqlExpr]  --- | Data type for SQL INSERT statements.-data SqlInsert  = SqlInsert      SqlTable [SqlColumn] [SqlExpr]+data SqlInsert  = SqlInsert SqlTable [SqlColumn] (NEL.NonEmpty [SqlExpr])
src/Opaleye/Internal/HaskellDB/Sql/Default.hs view
@@ -5,15 +5,18 @@ module Opaleye.Internal.HaskellDB.Sql.Default  where  import Opaleye.Internal.HaskellDB.PrimQuery+import qualified Opaleye.Internal.HaskellDB.PrimQuery as PQ import Opaleye.Internal.HaskellDB.Sql import Opaleye.Internal.HaskellDB.Sql.Generate+import qualified Opaleye.Internal.HaskellDB.Sql as Sql import Opaleye.Internal.Tag (tagWith) import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as BS8 import qualified Data.ByteString.Base16 as Base16+import qualified Data.List.NonEmpty as NEL  mkSqlGenerator :: SqlGenerator -> SqlGenerator-mkSqlGenerator gen = SqlGenerator +mkSqlGenerator gen = SqlGenerator     {      sqlUpdate      = defaultSqlUpdate      gen,      sqlDelete      = defaultSqlDelete      gen,@@ -28,43 +31,52 @@   toSqlOrder :: SqlGenerator -> OrderExpr -> (SqlExpr,SqlOrder)-toSqlOrder gen (OrderExpr o e) = (sqlExpr gen e, o')-    where o' = case o of-                 OpAsc  -> SqlAsc-                 OpDesc -> SqlDesc+toSqlOrder gen (OrderExpr o e) =+  (sqlExpr gen e, Sql.SqlOrder { sqlOrderDirection = o'+                               , sqlOrderNulls     = orderNulls' })+    where o' = case PQ.orderDirection o of+            PQ.OpAsc  -> Sql.SqlAsc+            PQ.OpDesc -> Sql.SqlDesc+          orderNulls' = case PQ.orderNulls o of+            PQ.NullsFirst -> Sql.SqlNullsFirst+            PQ.NullsLast  -> Sql.SqlNullsLast ++toSqlColumn :: Attribute -> SqlColumn+toSqlColumn attr = SqlColumn attr+ toSqlAssoc :: SqlGenerator -> Assoc -> [(SqlColumn,SqlExpr)]-toSqlAssoc gen = map (\(attr,expr) -> (SqlColumn attr, sqlExpr gen expr))+toSqlAssoc gen = map (\(attr,expr) -> (toSqlColumn attr, sqlExpr gen expr))  -defaultSqlUpdate :: SqlGenerator +defaultSqlUpdate :: SqlGenerator                  -> TableName  -- ^ Name of the table to update.-	         -> [PrimExpr] -- ^ Conditions which must all be true for a row+                 -> [PrimExpr] -- ^ Conditions which must all be true for a row                                --   to be updated.                  -> Assoc -- ^ Update the data with this.-	         -> SqlUpdate+                 -> SqlUpdate defaultSqlUpdate gen name criteria assigns-        = SqlUpdate name (toSqlAssoc gen assigns) (map (sqlExpr gen) criteria) +        = SqlUpdate (SqlTable name) (toSqlAssoc gen assigns) (map (sqlExpr gen) criteria)  -defaultSqlInsert :: SqlGenerator -                 -> TableName -- ^ Name of the table-	         -> Assoc -- ^ What to insert.-	         -> SqlInsert-defaultSqlInsert gen table assoc = SqlInsert table cs es-    where (cs,es) = unzip (toSqlAssoc gen assoc)-+defaultSqlInsert :: SqlGenerator+                 -> TableName+                 -> [Attribute]+                 -> NEL.NonEmpty [PrimExpr]+                 -> SqlInsert+defaultSqlInsert gen name attrs exprs =+  SqlInsert (SqlTable name) (map toSqlColumn attrs) ((fmap . map) (sqlExpr gen) exprs) -defaultSqlDelete :: SqlGenerator +defaultSqlDelete :: SqlGenerator                  -> TableName -- ^ Name of the table-	         -> [PrimExpr] -- ^ Criteria which must all be true for a row+                 -> [PrimExpr] -- ^ Criteria which must all be true for a row                                --   to be deleted.-	         -> SqlDelete-defaultSqlDelete gen name criteria = SqlDelete name (map (sqlExpr gen) criteria)+                 -> SqlDelete+defaultSqlDelete gen name criteria = SqlDelete (SqlTable name) (map (sqlExpr gen) criteria)   defaultSqlExpr :: SqlGenerator -> PrimExpr -> SqlExpr-defaultSqlExpr gen expr = +defaultSqlExpr gen expr =     case expr of       AttrExpr (Symbol a t) -> ColumnSqlExpr (SqlColumn (tagWith t a))       BaseTableAttrExpr a -> ColumnSqlExpr (SqlColumn a)@@ -99,39 +111,49 @@                                 UnOpFun     -> FunSqlExpr op' [e']                                 UnOpPrefix  -> PrefixSqlExpr op' (ParensSqlExpr e')                                 UnOpPostfix -> PostfixSqlExpr op' e'+      -- TODO: The current arrangement whereby the delimeter parameter+      -- of string_agg is in the AggrStringAggr constructor, but the+      -- parameter being aggregated is not, seems unsatisfactory+      -- because it leads to a non-uniformity of treatment, as seen+      -- below.  Perhaps we should have just `AggrExpr AggrOp` and+      -- always put the `PrimExpr` in the `AggrOp`.       AggrExpr op e    -> let op' = showAggrOp op                               e' = sqlExpr gen e-                           in AggrFunSqlExpr op' [e']+                              moreAggrFunParams = case op of+                                AggrStringAggr primE -> [sqlExpr gen primE]+                                _ -> []+                           in AggrFunSqlExpr op' (e' : moreAggrFunParams)       ConstExpr l      -> ConstSqlExpr (sqlLiteral gen l)-      CaseExpr cs e    -> let cs' = [(sqlExpr gen c, sqlExpr gen x)| (c,x) <- cs] +      CaseExpr cs e    -> let cs' = [(sqlExpr gen c, sqlExpr gen x)| (c,x) <- cs]                               e'  = sqlExpr gen e                            in CaseSqlExpr cs' e'       ListExpr es      -> ListSqlExpr (map (sqlExpr gen) es)       ParamExpr n _    -> ParamSqlExpr n PlaceHolderSqlExpr       FunExpr n exprs  -> FunSqlExpr n (map (sqlExpr gen) exprs)       CastExpr typ e1 -> CastSqlExpr typ (sqlExpr gen e1)+      DefaultInsertExpr -> DefaultSqlExpr  showBinOp :: BinOp -> String-showBinOp  OpEq         = "=" -showBinOp  OpLt         = "<" -showBinOp  OpLtEq       = "<=" -showBinOp  OpGt         = ">" -showBinOp  OpGtEq       = ">=" -showBinOp  OpNotEq      = "<>" -showBinOp  OpAnd        = "AND"  -showBinOp  OpOr         = "OR" -showBinOp  OpLike       = "LIKE" -showBinOp  OpIn         = "IN" +showBinOp  OpEq         = "="+showBinOp  OpLt         = "<"+showBinOp  OpLtEq       = "<="+showBinOp  OpGt         = ">"+showBinOp  OpGtEq       = ">="+showBinOp  OpNotEq      = "<>"+showBinOp  OpAnd        = "AND"+showBinOp  OpOr         = "OR"+showBinOp  OpLike       = "LIKE"+showBinOp  OpIn         = "IN" showBinOp  (OpOther s)  = s-showBinOp  OpCat        = "||" -showBinOp  OpPlus       = "+" -showBinOp  OpMinus      = "-" -showBinOp  OpMul        = "*" -showBinOp  OpDiv        = "/" -showBinOp  OpMod        = "MOD" -showBinOp  OpBitNot     = "~" -showBinOp  OpBitAnd     = "&" -showBinOp  OpBitOr      = "|" +showBinOp  OpCat        = "||"+showBinOp  OpPlus       = "+"+showBinOp  OpMinus      = "-"+showBinOp  OpMul        = "*"+showBinOp  OpDiv        = "/"+showBinOp  OpMod        = "MOD"+showBinOp  OpBitNot     = "~"+showBinOp  OpBitAnd     = "&"+showBinOp  OpBitOr      = "|" showBinOp  OpBitXor     = "^" showBinOp  OpAsg        = "=" @@ -151,22 +173,24 @@   showAggrOp :: AggrOp -> String-showAggrOp AggrCount    = "COUNT" -showAggrOp AggrSum      = "SUM" -showAggrOp AggrAvg      = "AVG" -showAggrOp AggrMin      = "MIN" -showAggrOp AggrMax      = "MAX" -showAggrOp AggrStdDev   = "StdDev" -showAggrOp AggrStdDevP  = "StdDevP" -showAggrOp AggrVar      = "Var" -showAggrOp AggrVarP     = "VarP"                -showAggrOp AggrBoolAnd  = "BOOL_AND"-showAggrOp AggrBoolOr   = "BOOL_OR"-showAggrOp (AggrOther s)        = s+showAggrOp AggrCount          = "COUNT"+showAggrOp AggrSum            = "SUM"+showAggrOp AggrAvg            = "AVG"+showAggrOp AggrMin            = "MIN"+showAggrOp AggrMax            = "MAX"+showAggrOp AggrStdDev         = "StdDev"+showAggrOp AggrStdDevP        = "StdDevP"+showAggrOp AggrVar            = "Var"+showAggrOp AggrVarP           = "VarP"+showAggrOp AggrBoolAnd        = "BOOL_AND"+showAggrOp AggrBoolOr         = "BOOL_OR"+showAggrOp AggrArr            = "ARRAY_AGG"+showAggrOp (AggrStringAggr _) = "STRING_AGG"+showAggrOp (AggrOther s)      = s   defaultSqlLiteral :: SqlGenerator -> Literal -> String-defaultSqlLiteral _ l = +defaultSqlLiteral _ l =     case l of       NullLit       -> "NULL"       DefaultLit    -> "DEFAULT"@@ -186,7 +210,7 @@ -- | Quote a string and escape characters that need escaping --   We use Postgres "escape strings", i.e. strings prefixed --   with E, to ensure that escaping with backslash is valid.-quote :: String -> String +quote :: String -> String quote s = "E'" ++ concatMap escape s ++ "'"  -- | Escape characters that need escaping
src/Opaleye/Internal/HaskellDB/Sql/Generate.hs view
@@ -7,12 +7,13 @@ import Opaleye.Internal.HaskellDB.PrimQuery import Opaleye.Internal.HaskellDB.Sql +import qualified Data.List.NonEmpty as NEL  data SqlGenerator = SqlGenerator     {      sqlUpdate      :: TableName -> [PrimExpr] -> Assoc -> SqlUpdate,      sqlDelete      :: TableName -> [PrimExpr] -> SqlDelete,-     sqlInsert      :: TableName -> Assoc -> SqlInsert,+     sqlInsert      :: TableName -> [Attribute] -> NEL.NonEmpty [PrimExpr] -> SqlInsert,      sqlExpr        :: PrimExpr -> SqlExpr,      sqlLiteral     :: Literal -> String,      -- | Turn a string into a quoted string. Quote characters
src/Opaleye/Internal/HaskellDB/Sql/Print.hs view
@@ -2,24 +2,27 @@ --                HWT Group (c) 2003, haskelldb-users@lists.sourceforge.net -- License     :  BSD-style -module Opaleye.Internal.HaskellDB.Sql.Print ( +module Opaleye.Internal.HaskellDB.Sql.Print (                                      ppUpdate,-                                     ppDelete, +                                     ppDelete,                                      ppInsert,                                      ppSqlExpr,                                      ppWhere,                                      ppGroupBy,                                      ppOrderBy,+                                     ppTable,                                      ppAs,                                      commaV,                                      commaH-	                            ) where+                                    ) where  import Opaleye.Internal.HaskellDB.Sql (SqlColumn(..), SqlDelete(..),                                SqlExpr(..), SqlOrder(..), SqlInsert(..),-                               SqlUpdate(..))+                               SqlUpdate(..), SqlTable(..))+import qualified Opaleye.Internal.HaskellDB.Sql as Sql  import Data.List (intersperse)+import qualified Data.List.NonEmpty as NEL import Text.PrettyPrint.HughesPJ (Doc, (<+>), ($$), (<>), comma, doubleQuotes,                                   empty, equals, hcat, hsep, parens, punctuate,                                   text, vcat)@@ -27,7 +30,7 @@  ppWhere :: [SqlExpr] -> Doc ppWhere [] = empty-ppWhere es = text "WHERE" +ppWhere es = text "WHERE"              <+> hsep (intersperse (text "AND")                        (map (parens . ppSqlExpr) es)) @@ -38,25 +41,41 @@     ppGroupAttrs cs = commaV nameOrExpr cs     nameOrExpr :: SqlExpr -> Doc     nameOrExpr (ColumnSqlExpr (SqlColumn col)) = text col-    nameOrExpr expr = parens (ppSqlExpr expr)-    +    -- Silliness to avoid "ORDER BY 1" etc. meaning order by the first column+    -- Any identity function will do+    --  nameOrExpr expr = parens (ppSqlExpr expr)+    nameOrExpr expr = text "COALESCE" <+> parens (ppSqlExpr expr)+ ppOrderBy :: [(SqlExpr,SqlOrder)] -> Doc ppOrderBy [] = empty ppOrderBy ord = text "ORDER BY" <+> commaV ppOrd ord     where-      ppOrd (e,o) = ppSqlExpr e <+> ppSqlOrder o-      ppSqlOrder :: SqlOrder -> Doc-      ppSqlOrder SqlAsc = text "ASC"-      ppSqlOrder SqlDesc = text "DESC"+    -- Silliness to avoid "ORDER BY 1" etc. meaning order by the first column+    -- Any identity function will do+    --   ppOrd (e,o) = ppSqlExpr e <+> ppSqlDirection o <+> ppSqlNulls o+      ppOrd (e,o) = text "COALESCE"+                      <+> parens (ppSqlExpr e)+                      <+> ppSqlDirection o+                      <+> ppSqlNulls o +ppSqlDirection :: Sql.SqlOrder -> Doc+ppSqlDirection x = text $ case Sql.sqlOrderDirection x of+  Sql.SqlAsc  -> "ASC"+  Sql.SqlDesc -> "DESC"++ppSqlNulls :: Sql.SqlOrder -> Doc+ppSqlNulls x = text $ case Sql.sqlOrderNulls x of+        Sql.SqlNullsFirst -> "NULLS FIRST"+        Sql.SqlNullsLast  -> "NULLS LAST"+ ppAs :: String -> Doc -> Doc-ppAs alias expr    | null alias    = expr                               -                   | otherwise     = expr <+> (hsep . map text) ["as",alias]+ppAs alias expr    | null alias    = expr+                   | otherwise     = expr <+> hsep [text "as", doubleQuotes (text alias)]   ppUpdate :: SqlUpdate -> Doc-ppUpdate (SqlUpdate name assigns criteria)-        = text "UPDATE" <+> text name+ppUpdate (SqlUpdate table assigns criteria)+        = text "UPDATE" <+> ppTable table         $$ text "SET" <+> commaV ppAssign assigns         $$ ppWhere criteria     where@@ -64,16 +83,16 @@   ppDelete :: SqlDelete -> Doc-ppDelete (SqlDelete name criteria) =-    text "DELETE FROM" <+> text name $$ ppWhere criteria+ppDelete (SqlDelete table criteria) =+    text "DELETE FROM" <+> ppTable table $$ ppWhere criteria   ppInsert :: SqlInsert -> Doc- ppInsert (SqlInsert table names values)-    = text "INSERT INTO" <+> text table +    = text "INSERT INTO" <+> ppTable table       <+> parens (commaV ppColumn names)-      $$ text "VALUES" <+> parens (commaV ppSqlExpr values)+      $$ text "VALUES" <+> commaV (\v -> parens (commaV ppSqlExpr v))+                                  (NEL.toList values)  -- If we wanted to make the SQL slightly more readable this would be -- one easy place to do it.  Currently we wrap all column references@@ -83,13 +102,17 @@ ppColumn :: SqlColumn -> Doc ppColumn (SqlColumn s) = doubleQuotes (text s) +-- Postgres treats upper case letters in table names as lower case,+-- unless the name is quoted!+ppTable :: SqlTable -> Doc+ppTable (SqlTable s) = doubleQuotes (text s)  ppSqlExpr :: SqlExpr -> Doc ppSqlExpr expr =     case expr of       ColumnSqlExpr c     -> ppColumn c       ParensSqlExpr e -> parens (ppSqlExpr e)-      BinSqlExpr op e1 e2 -> ppSqlExpr e1 <+> text op <+> ppSqlExpr e2 +      BinSqlExpr op e1 e2 -> ppSqlExpr e1 <+> text op <+> ppSqlExpr e2       PrefixSqlExpr op e  -> text op <+> ppSqlExpr e       PostfixSqlExpr op e -> ppSqlExpr e <+> text op       FunSqlExpr f es     -> text f <> parens (commaH ppSqlExpr es)@@ -97,13 +120,13 @@       ConstSqlExpr c      -> text c       CaseSqlExpr cs el   -> text "CASE" <+> vcat (map ppWhen cs)                              <+> text "ELSE" <+> ppSqlExpr el <+> text "END"-          where ppWhen (w,t) = text "WHEN" <+> ppSqlExpr w +          where ppWhen (w,t) = text "WHEN" <+> ppSqlExpr w                                <+> text "THEN" <+> ppSqlExpr t       ListSqlExpr es      -> parens (commaH ppSqlExpr es)       ParamSqlExpr _ v -> ppSqlExpr v       PlaceHolderSqlExpr -> text "?"       CastSqlExpr typ e -> text "CAST" <> parens (ppSqlExpr e <+> text "AS" <+> text typ)-    +      DefaultSqlExpr    -> text "DEFAULT"  commaH :: (a -> Doc) -> [a] -> Doc commaH f = hcat . punctuate comma . map f
src/Opaleye/Internal/Helpers.hs view
@@ -14,3 +14,8 @@  (.::) :: (r -> z) -> (a -> b -> c -> d -> r) -> a -> b -> c -> d -> z (.::) f g a b c d = f (g a b c d)++infixr 8 .::.++(.::.) :: (r -> z) -> (a -> b -> c -> d -> e -> r) -> a -> b -> c -> d -> e -> z+(.::.) f g a b c d e = f (g a b c d e)
src/Opaleye/Internal/Join.hs view
@@ -13,7 +13,7 @@  import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ -data NullMaker a b = NullMaker (a -> b)+newtype NullMaker a b = NullMaker (a -> b)  toNullable :: NullMaker a b -> a -> b toNullable (NullMaker f) = f@@ -23,10 +23,10 @@ extractLeftJoinFields n = PM.extractAttr ("result" ++ show n ++ "_")  instance D.Default NullMaker (Column a) (Column (Nullable a)) where-  def = NullMaker C.unsafeCoerce+  def = NullMaker C.unsafeCoerceColumn  instance D.Default NullMaker (Column (Nullable a)) (Column (Nullable a)) where-  def = NullMaker C.unsafeCoerce+  def = NullMaker C.unsafeCoerceColumn  -- { Boilerplate instances 
src/Opaleye/Internal/Order.hs view
@@ -7,13 +7,10 @@  import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ import qualified Data.Functor.Contravariant as C+import qualified Data.Functor.Contravariant.Divisible as Divisible import qualified Data.Profunctor as P import qualified Data.Monoid as M--data SingleOrder a = SingleOrder HPQ.OrderOp (a -> HPQ.PrimExpr)--instance C.Contravariant SingleOrder where-  contramap f (SingleOrder op g) = SingleOrder op (P.lmap f g)+import qualified Data.Void as Void  {-| An `Order` represents an expression to order on and a sort@@ -21,24 +18,37 @@ `Data.Monoid.mappend`.  If two rows are equal according to the first `Order`, the second is used, and so on. -}-newtype Order a = Order [SingleOrder a] +-- Like the (columns -> RowParser haskells) field of QueryRunner this+-- type is "too big".  We never actually look at the 'a' (in the+-- QueryRunner case the 'colums') except to check the "structure".+-- This is so we can support a SumProfunctor instance.+newtype Order a = Order (a -> [(HPQ.OrderOp, HPQ.PrimExpr)])+ instance C.Contravariant Order where-  contramap f (Order xs) = Order (fmap (C.contramap f) xs)+  contramap f (Order g) = Order (P.lmap f g)  instance M.Monoid (Order a) where   mempty = Order M.mempty   Order o `mappend` Order o' = Order (o `M.mappend` o') +instance Divisible.Divisible Order where+  divide f o o' = M.mappend (C.contramap (fst . f) o)+                            (C.contramap (snd . f) o')+  conquer = M.mempty++instance Divisible.Decidable Order where+  lose f = C.contramap f (Order Void.absurd)+  choose f (Order o) (Order o') = C.contramap f (Order (either o o'))+ order :: HPQ.OrderOp -> (a -> C.Column b) -> Order a-order op f = C.contramap f (Order [SingleOrder op IC.unColumn])+order op f = Order (fmap (\column -> [(op, IC.unColumn column)]) f)  orderByU :: Order a -> (a, PQ.PrimQuery, T.Tag) -> (a, PQ.PrimQuery, T.Tag) orderByU os (columns, primQ, t) = (columns, primQ', t)   where primQ' = PQ.Order orderExprs primQ         Order sos = os-        orderExprs = map (\(SingleOrder op f)-                          -> HPQ.OrderExpr op (f columns)) sos+        orderExprs = map (uncurry HPQ.OrderExpr) (sos columns)  limit' :: Int -> (a, PQ.PrimQuery, T.Tag) -> (a, PQ.PrimQuery, T.Tag) limit' n (x, q, t) = (x, PQ.Limit (PQ.LimitOp n) q, t)
+ src/Opaleye/Internal/PGTypes.hs view
@@ -0,0 +1,30 @@+module Opaleye.Internal.PGTypes where++import           Opaleye.Internal.Column (Column(Column))+import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ++import qualified Data.Text as SText+import qualified Data.Text.Encoding as STextEncoding+import qualified Data.Text.Lazy as LText+import qualified Data.Text.Lazy.Encoding as LTextEncoding+import qualified Data.ByteString as SByteString+import qualified Data.ByteString.Lazy as LByteString+import qualified Data.Time as Time+import qualified Data.Time.Locale.Compat as Locale++unsafePgFormatTime :: Time.FormatTime t => HPQ.Name -> String -> t -> Column c+unsafePgFormatTime typeName formatString = castToType typeName . format+  where format = Time.formatTime Locale.defaultTimeLocale formatString++literalColumn :: HPQ.Literal -> Column a+literalColumn = Column . HPQ.ConstExpr++castToType :: HPQ.Name -> String -> Column c+castToType typeName =+    Column . HPQ.CastExpr typeName . HPQ.ConstExpr . HPQ.OtherLit++strictDecodeUtf8 :: SByteString.ByteString -> String+strictDecodeUtf8 = SText.unpack . STextEncoding.decodeUtf8++lazyDecodeUtf8 :: LByteString.ByteString -> String+lazyDecodeUtf8 = LText.unpack . LTextEncoding.decodeUtf8
src/Opaleye/Internal/PackMap.hs view
@@ -28,18 +28,41 @@ -- and share them between many different restrictions of f.  For -- example, TableColumnMaker is like a Setter so we would restrict f -- to the Distributive case.++-- | A 'PackMap' @a@ @b@ @s@ @t@ encodes how an @s@ contains an+-- updatable sequence of @a@ inside it.  Each @a@ in the sequence can+-- be updated to a @b@ (and the @s@ changes to a @t@ to reflect this+-- change of type).+--+-- 'PackMap' is just like a @Traversal@ from the lens package.+-- 'PackMap' has a different order of arguments to @Traversal@ because+-- it typically needs to be made a 'Profunctor' (and indeed+-- 'ProductProfunctor') in @s@ and @t@.  It is unclear at this point+-- whether we want the same @Traversal@ laws to hold or not.  Our use+-- cases may be much more general. data PackMap a b s t = PackMap (Applicative f =>                                 (a -> f b) -> s -> f t) -packmap :: Applicative f => PackMap a b s t -> (a -> f b) -> s -> f t-packmap (PackMap f) = f+-- | Replaces the targeted occurences of @a@ in @s@ with @b@ (changing+-- the @s@ to a @t@ in the process).  This can be done via an+-- 'Applicative' action.+--+-- 'traversePM' is just like @traverse@ from the @lens@ package.+-- 'traversePM' used to be called @packmap@.+traversePM :: Applicative f => PackMap a b s t -> (a -> f b) -> s -> f t+traversePM (PackMap f) = f -over :: PackMap a b s t -> (a -> b) -> s -> t-over p f = I.runIdentity . packmap p (I.Identity . f)+-- | Modify the targeted occurrences of @a@ in @s@ with @b@ (changing+-- the @s@ to a @t@ in the process).+--+-- 'overPM' is just like @over@ from the @lens@ pacakge.+overPM :: PackMap a b s t -> (a -> b) -> s -> t+overPM p f = I.runIdentity . traversePM p (I.Identity . f)  --- { A helpful monad for writing columns in the AST+-- { +-- | A helpful monad for writing columns in the AST type PM a = State.State (a, Int)  new :: PM a String@@ -62,23 +85,35 @@  -- { General functions for writing columns in the AST --- This one ignores the 'a' when making the internal column name.-extractAttr :: String -> T.Tag -> a-               -> PM [(HPQ.Symbol, a)] HPQ.PrimExpr-extractAttr s = extractAttrPE (const (s ++))---- This one can make the internal column name depend on the 'a' in--- question (probably a PrimExpr)-extractAttrPE :: (a -> String -> String) -> T.Tag -> a-               -> PM [(HPQ.Symbol, a)] HPQ.PrimExpr+-- | Make a fresh name for an input value (the variable @primExpr@+-- type is typically actually a 'HPQ.PrimExpr') based on the supplied+-- function and the unique 'T.Tag' that is used as part of our+-- @QueryArr@.+--+-- Add the fresh name and the input value it refers to to the list in+-- the state parameter.+extractAttrPE :: (primExpr -> String -> String) -> T.Tag -> primExpr+               -> PM [(HPQ.Symbol, primExpr)] HPQ.PrimExpr extractAttrPE mkName t pe = do   i <- new   let s = HPQ.Symbol (mkName pe i) t   write (s, pe)   return (HPQ.AttrExpr s) +-- | As 'extractAttrPE' but ignores the 'primExpr' when making the+-- fresh column name and just uses the supplied 'String' and 'T.Tag'.+extractAttr :: String -> T.Tag -> primExpr+               -> PM [(HPQ.Symbol, primExpr)] HPQ.PrimExpr+extractAttr s = extractAttrPE (const (s ++))+ -- } +eitherFunction :: Functor f+               => (a -> f b)+               -> (a' -> f b')+               -> Either a a'+               -> f (Either b b')+eitherFunction f g = fmap (either (fmap Left) (fmap Right)) (f PP.+++! g)  -- { @@ -98,5 +133,10 @@ instance ProductProfunctor (PackMap a b) where   empty = PP.defaultEmpty   (***!) = PP.defaultProfunctorProduct++instance PP.SumProfunctor (PackMap a b) where+  f +++! g = (PackMap (\x -> eitherFunction (f' x) (g' x)))+    where PackMap f' = f+          PackMap g' = g  -- }
src/Opaleye/Internal/PrimQuery.hs view
@@ -23,7 +23,7 @@                | Aggregate [(Symbol, (Maybe HPQ.AggrOp, HPQ.PrimExpr))] PrimQuery                | Order [HPQ.OrderExpr] PrimQuery                | Limit LimitOp PrimQuery-               | Join JoinType [(Symbol, HPQ.PrimExpr)] HPQ.PrimExpr PrimQuery PrimQuery+               | Join JoinType HPQ.PrimExpr PrimQuery PrimQuery                | Values [Symbol] [[HPQ.PrimExpr]]                | Binary BinOp [(Symbol, (HPQ.PrimExpr, HPQ.PrimExpr))] (PrimQuery, PrimQuery)                  deriving Show@@ -34,7 +34,7 @@                        , [(Symbol, (Maybe HPQ.AggrOp, HPQ.PrimExpr))] -> p -> p                        , [HPQ.OrderExpr] -> p -> p                        , LimitOp -> p -> p-                       , JoinType -> [(Symbol, HPQ.PrimExpr)] -> HPQ.PrimExpr -> p -> p -> p+                       , JoinType -> HPQ.PrimExpr -> p -> p -> p                        , [Symbol] -> [[HPQ.PrimExpr]] -> p                        , BinOp -> [(Symbol, (HPQ.PrimExpr, HPQ.PrimExpr))] -> (p, p) -> p                        )@@ -49,7 +49,7 @@           Aggregate aggrs pq         -> aggregate aggrs (self pq)           Order pes pq               -> order pes (self pq)           Limit op pq                -> limit op (self pq)-          Join j pes cond q1 q2      -> join j pes cond (self q1) (self q2)+          Join j cond q1 q2          -> join j cond (self q1) (self q2)           Values ss pes              -> values ss pes           Binary binop pes (pq, pq') -> binary binop pes (self pq, self pq')         fix f = let x = f x in x
src/Opaleye/Internal/Print.hs view
@@ -14,10 +14,13 @@  import           Text.PrettyPrint.HughesPJ (Doc, ($$), (<+>), text, empty,                                             parens)+import qualified Data.List.NonEmpty as NEL +type TableAlias = String+ ppSql :: Select -> Doc ppSql (SelectFrom s) = ppSelectFrom s-ppSql (Table name) = text name+ppSql (Table table) = HPrint.ppTable table ppSql (SelectJoin j) = ppSelectJoin j ppSql (SelectValues v) = ppSelectValues v ppSql (SelectBinary v) = ppSelectBinary v@@ -34,8 +37,7 @@   ppSelectJoin :: Join -> Doc-ppSelectJoin j = text "SELECT"-                 <+> ppAttrs (Sql.jAttrs j)+ppSelectJoin j = text "SELECT *"                  $$  text "FROM"                  $$  ppTable (tableAlias 1 s1)                  $$  ppJoinType (Sql.jJoinType j)@@ -58,9 +60,9 @@ ppJoinType :: Sql.JoinType -> Doc ppJoinType Sql.LeftJoin = text "LEFT OUTER JOIN" -ppAttrs :: [(HSql.SqlExpr, Maybe HSql.SqlColumn)] -> Doc-ppAttrs [] = text "*"-ppAttrs xs = HPrint.commaV nameAs xs+ppAttrs :: Sql.SelectAttrs -> Doc+ppAttrs Sql.Star             = text "*"+ppAttrs (Sql.SelectAttrs xs) = (HPrint.commaV nameAs . NEL.toList) xs  -- This is pretty much just nameAs from HaskellDB nameAs :: (HSql.SqlExpr, Maybe HSql.SqlColumn) -> Doc@@ -71,21 +73,21 @@ ppTables [] = empty ppTables ts = text "FROM" <+> HPrint.commaV ppTable (zipWith tableAlias [1..] ts) -tableAlias :: Int -> Select -> (HSql.SqlTable, Select)+tableAlias :: Int -> Select -> (TableAlias, Select) tableAlias i select = ("T" ++ show i, select)  -- TODO: duplication with ppSql-ppTable :: (HSql.SqlTable, Select) -> Doc+ppTable :: (TableAlias, Select) -> Doc ppTable (alias, select) = case select of-  Table name -> HPrint.ppAs alias (text name)+  Table table -> HPrint.ppAs alias (HPrint.ppTable table)   SelectFrom selectFrom -> HPrint.ppAs alias (parens (ppSelectFrom selectFrom))   SelectJoin slj -> HPrint.ppAs alias (parens (ppSelectJoin slj))   SelectValues slv -> HPrint.ppAs alias (parens (ppSelectValues slv))   SelectBinary slb -> HPrint.ppAs alias (parens (ppSelectBinary slb)) -ppGroupBy :: [HSql.SqlExpr] -> Doc-ppGroupBy [] = empty-ppGroupBy xs = HPrint.ppGroupBy xs+ppGroupBy :: Maybe (NEL.NonEmpty HSql.SqlExpr) -> Doc+ppGroupBy Nothing   = empty+ppGroupBy (Just xs) = HPrint.ppGroupBy (NEL.toList xs)  ppLimit :: Maybe Int -> Doc ppLimit Nothing = empty@@ -110,5 +112,11 @@ ppInsertReturning :: Sql.Returning HSql.SqlInsert -> Doc ppInsertReturning (Sql.Returning insert returnExprs) =   HPrint.ppInsert insert+  $$ text "RETURNING"+  <+> HPrint.commaV HPrint.ppSqlExpr returnExprs++ppUpdateReturning :: Sql.Returning HSql.SqlUpdate -> Doc+ppUpdateReturning (Sql.Returning update returnExprs) =+  HPrint.ppUpdate update   $$ text "RETURNING"   <+> HPrint.commaV HPrint.ppSqlExpr returnExprs
src/Opaleye/Internal/QueryArr.hs view
@@ -31,13 +31,14 @@ runSimpleQueryArr :: QueryArr a b -> (a, Tag) -> (b, PQ.PrimQuery, Tag) runSimpleQueryArr f (a, t) = runQueryArr f (a, PQ.Unit, t) +runSimpleQueryArrStart :: QueryArr a b -> a -> (b, PQ.PrimQuery, Tag)+runSimpleQueryArrStart q a = runSimpleQueryArr q (a, Tag.start)+ runQueryArrUnpack :: U.Unpackspec a b                   -> Query a -> ([HPQ.PrimExpr], PQ.PrimQuery, Tag) runQueryArrUnpack unpackspec q = (primExprs, primQ, endTag)-  where (columns, primQ, endTag) = runSimpleQueryArr q ((), Tag.start)-        f pe = ([pe], pe)-        primExprs :: [HPQ.PrimExpr]-        (primExprs, _) = U.runUnpackspec unpackspec f columns+  where (columns, primQ, endTag) = runSimpleQueryArrStart q ()+        primExprs = U.collectPEs unpackspec columns  first3 :: (a1 -> b) -> (a1, a2, a3) -> (b, a2, a3) first3 f (a1, a2, a3) = (f a1, a2, a3)
src/Opaleye/Internal/RunQuery.hs view
@@ -2,7 +2,7 @@  module Opaleye.Internal.RunQuery where -import           Control.Applicative (Applicative, pure, (<*>))+import           Control.Applicative (Applicative, pure, (<*>), liftA2)  import           Database.PostgreSQL.Simple.Internal (RowParser) import           Database.PostgreSQL.Simple.FromField (FieldParser, FromField,@@ -12,9 +12,11 @@  import           Opaleye.Column (Column) import           Opaleye.Internal.Column (Nullable)+import qualified Opaleye.Internal.PackMap as PackMap import qualified Opaleye.Column as C import qualified Opaleye.Internal.Unpackspec as U import qualified Opaleye.PGTypes as T+import qualified Opaleye.Internal.PGTypes as IPT (strictDecodeUtf8)  import qualified Data.Profunctor as P import           Data.Profunctor (dimap)@@ -28,6 +30,7 @@ import qualified Data.ByteString as SBS import qualified Data.ByteString.Lazy as LBS import qualified Data.Time as Time+import qualified Data.String as String import           Data.UUID (UUID) import           GHC.Int (Int64) @@ -48,27 +51,45 @@  -- } --- We introduce 'QueryRunnerColumn' which is *not* a Product--- Profunctor because it is the only way I know of to get the instance--- generation to work for non-Nullable and Nullable types at once.-data QueryRunnerColumn coltype haskell =-  QueryRunnerColumn (U.Unpackspec (Column coltype) ()) (FieldParser haskell)+-- | A 'QueryRunnerColumn' @pgType@ @haskellType@ encodes how to turn+-- a value of Postgres type @pgType@ into a value of Haskell type+-- @haskellType@.  For example a value of type 'QueryRunnerColumn'+-- 'T.PGText' 'String' encodes how to turn a 'PGText' result from the+-- database into a Haskell 'String'. -data QueryRunner columns haskells = QueryRunner (U.Unpackspec columns ())-                                                (RowParser haskells)+-- This is *not* a Product Profunctor because it is the only way I+-- know of to get the instance generation to work for non-Nullable and+-- Nullable types at once.+data QueryRunnerColumn pgType haskellType =+  QueryRunnerColumn (U.Unpackspec (Column pgType) ()) (FieldParser haskellType) +data QueryRunner columns haskells =+  QueryRunner (U.Unpackspec columns ())+              (columns -> RowParser haskells)+              -- We never actually+              -- look at the columns+              -- except to see its+              -- "type" in the case+              -- of a sum profunctor+              (columns -> Bool)+              -- ^ Have we actually requested any columns?  If we+              -- asked for zero columns then the SQL generator will+              -- have to put a dummy 0 into the SELECT statement,+              -- since we can't select zero columns.  In that case we+              -- have to make sure we read a single Int.+ fieldQueryRunnerColumn :: FromField haskell => QueryRunnerColumn coltype haskell fieldQueryRunnerColumn =   QueryRunnerColumn (P.rmap (const ()) U.unpackspecColumn) fromField  queryRunner :: QueryRunnerColumn a b -> QueryRunner (Column a) b-queryRunner qrc = QueryRunner u (fieldWith fp)+queryRunner qrc = QueryRunner u (const (fieldWith fp)) (const True)     where QueryRunnerColumn u fp = qrc  queryRunnerColumnNullable :: QueryRunnerColumn a b                        -> QueryRunnerColumn (Nullable a) (Maybe b) queryRunnerColumnNullable qr =-  QueryRunnerColumn (P.lmap C.unsafeCoerce u) (fromField' fp)+  QueryRunnerColumn (P.lmap C.unsafeCoerceColumn u) (fromField' fp)   where QueryRunnerColumn u fp = qr         fromField' :: FieldParser a -> FieldParser (Maybe a)         fromField' _ _ Nothing = pure Nothing@@ -89,8 +110,11 @@ -- { Instances that must be provided once for each type.  Instances --   for Nullable are derived automatically from these. -class QueryRunnerColumnDefault a b where-  queryRunnerColumnDefault :: QueryRunnerColumn a b+-- | A 'QueryRunnerColumnDefault' @pgType@ @haskellType@ represents+-- the default way to turn a @pgType@ result from the database into a+-- Haskell value of type @haskelType@.+class QueryRunnerColumnDefault pgType haskellType where+  queryRunnerColumnDefault :: QueryRunnerColumn pgType haskellType  instance QueryRunnerColumnDefault T.PGInt4 Int where   queryRunnerColumnDefault = fieldQueryRunnerColumn@@ -140,10 +164,18 @@ instance QueryRunnerColumnDefault T.PGCitext (CI.CI LT.Text) where   queryRunnerColumnDefault = fieldQueryRunnerColumn +instance QueryRunnerColumnDefault T.PGJson String where+  queryRunnerColumnDefault =+    QueryRunnerColumn (P.rmap (const ()) U.unpackspecColumn) jsonFieldParser++instance QueryRunnerColumnDefault T.PGJsonb String where+  queryRunnerColumnDefault =+    QueryRunnerColumn (P.rmap (const ()) U.unpackspecColumn) jsonbFieldParser+ -- No CI String instance since postgresql-simple doesn't define FromField (CI String)  arrayColumn :: Column (T.PGArray a) -> Column a-arrayColumn = C.unsafeCoerce+arrayColumn = C.unsafeCoerceColumn  instance (Typeable b, QueryRunnerColumnDefault a b) =>          QueryRunnerColumnDefault (T.PGArray a) [b] where@@ -155,21 +187,31 @@ -- Boilerplate instances  instance Functor (QueryRunner c) where-  fmap f (QueryRunner u r) = QueryRunner u (fmap f r)+  fmap f (QueryRunner u r b) = QueryRunner u ((fmap . fmap) f r) b  -- TODO: Seems like this one should be simpler! instance Applicative (QueryRunner c) where-  pure = QueryRunner (P.lmap (const ()) PP.empty) . pure-  QueryRunner uf rf <*> QueryRunner ux rx =-    QueryRunner (P.dimap (\x -> (x,x)) (const ()) (uf PP.***! ux)) (rf <*> rx)+  pure = flip (QueryRunner (P.lmap (const ()) PP.empty)) (const False)+         . pure+         . pure+  QueryRunner uf rf bf <*> QueryRunner ux rx bx =+    QueryRunner (P.dimap (\x -> (x,x)) (const ()) (uf PP.***! ux)) ((<*>) <$> rf <*> rx) (liftA2 (||) bf bx)  instance P.Profunctor QueryRunner where-  dimap f g (QueryRunner u r) = QueryRunner (P.lmap f u) (fmap g r)+  dimap f g (QueryRunner u r b) =+    QueryRunner (P.lmap f u) (P.dimap f (fmap g) r) (P.lmap f b)  instance PP.ProductProfunctor QueryRunner where   empty = PP.defaultEmpty   (***!) = PP.defaultProfunctorProduct +instance PP.SumProfunctor QueryRunner where+  f +++! g = QueryRunner (P.rmap (const ()) (fu PP.+++! gu))+                         (PackMap.eitherFunction fr gr)+                         (either fb gb)+    where QueryRunner fu fr fb = f+          QueryRunner gu gr gb = g+ -- }  -- { Annoying postgresql-simple patch.  Delete this when it is merged upstream.@@ -189,10 +231,10 @@           _ -> returnError Incompatible f ""  fromArray :: FieldParser a -> TypeInfo -> Field -> Parser (Conversion [a])-fromArray fieldParser typeInfo f = sequence . (parseIt <$>) <$> array delim+fromArray fieldParser tInfo f = sequence . (parseIt <$>) <$> array delim   where-    delim = typdelim (typelem typeInfo)-    fElem = f{ typeOid = typoid (typelem typeInfo) }+    delim = typdelim (typelem tInfo)+    fElem = f{ typeOid = typoid (typelem tInfo) }      parseIt item =         fieldParser f' $ if item' == fromString "NULL" then Nothing else Just item'@@ -200,5 +242,26 @@         item' = fmt delim item         f' | Arrays.Array _ <- item = f            | otherwise              = fElem++-- }++-- { Allow @postgresql-simple@ conversions from JSON types to 'String'++jsonFieldParser, jsonbFieldParser :: FieldParser String+jsonFieldParser  = jsonFieldTypeParser (String.fromString "json")+jsonbFieldParser = jsonFieldTypeParser (String.fromString "jsonb")++-- typenames, not type Oids are used in order to avoid creating+-- a dependency on 'Database.PostgreSQL.LibPQ'+jsonFieldTypeParser :: SBS.ByteString -> FieldParser String+jsonFieldTypeParser jsonTypeName field mData = do+    ti <- typeInfo field+    if TI.typname ti == jsonTypeName+       then convert+       else returnError Incompatible field "types incompatible"+  where+    convert = case mData of+        Just bs -> pure $ IPT.strictDecodeUtf8 bs+        _       -> returnError UnexpectedNull field ""  -- }
src/Opaleye/Internal/Sql.hs view
@@ -23,11 +23,16 @@             | SelectBinary Binary             deriving Show +data SelectAttrs =+    Star+  | SelectAttrs (NEL.NonEmpty (HSql.SqlExpr, Maybe HSql.SqlColumn))+  deriving Show+ data From = From {-  attrs     :: [(HSql.SqlExpr, Maybe HSql.SqlColumn)],+  attrs     :: SelectAttrs,   tables    :: [Select],   criteria  :: [HSql.SqlExpr],-  groupBy   :: [HSql.SqlExpr],+  groupBy   :: Maybe (NEL.NonEmpty HSql.SqlExpr),   orderBy   :: [(HSql.SqlExpr, HSql.SqlOrder)],   limit     :: Maybe Int,   offset    :: Maybe Int@@ -36,14 +41,13 @@  data Join = Join {   jJoinType   :: JoinType,-  jAttrs      :: [(HSql.SqlExpr, Maybe HSql.SqlColumn)],   jTables     :: (Select, Select),   jCond       :: HSql.SqlExpr   }                 deriving Show  data Values = Values {-  vAttrs  :: [(HSql.SqlExpr, Maybe HSql.SqlColumn)],+  vAttrs  :: SelectAttrs,   vValues :: [[HSql.SqlExpr]] } deriving Show @@ -65,19 +69,19 @@                      values, binary)  sql :: ([HPQ.PrimExpr], PQ.PrimQuery, T.Tag) -> Select-sql (pes, pq, t) = SelectFrom $ newSelect { attrs = makeAttrs pes+sql (pes, pq, t) = SelectFrom $ newSelect { attrs = SelectAttrs (ensureColumns (makeAttrs pes))                                           , tables = [pqSelect] }   where pqSelect = PQ.foldPrimQuery sqlQueryGenerator pq         makeAttrs = flip (zipWith makeAttr) [1..]         makeAttr pe i = sqlBinding (Symbol ("result" ++ show (i :: Int)) t, pe)  unit :: Select-unit = SelectFrom newSelect { attrs  = [(HSql.ConstSqlExpr "0", Nothing)] }+unit = SelectFrom newSelect { attrs  = SelectAttrs (ensureColumns []) }  baseTable :: String -> [(Symbol, HPQ.PrimExpr)] -> Select baseTable name columns = SelectFrom $-    newSelect { attrs = map sqlBinding columns-              , tables = [Table name] }+    newSelect { attrs = SelectAttrs (ensureColumns (map sqlBinding columns))+              , tables = [Table (HSql.SqlTable name)] }  product :: NEL.NonEmpty Select -> [HPQ.PrimExpr] -> Select product ss pes = SelectFrom $@@ -85,12 +89,27 @@               , criteria = map sqlExpr pes }  aggregate :: [(Symbol, (Maybe HPQ.AggrOp, HPQ.PrimExpr))] -> Select -> Select-aggregate aggrs s = SelectFrom $ newSelect { attrs = map attr aggrs+aggregate aggrs s = SelectFrom $ newSelect { attrs = SelectAttrs+                                               (ensureColumns (map attr aggrs))                                            , tables = [s]-                                           , groupBy = groupBy' }-  where groupBy' = (map sqlExpr+                                           , groupBy = (Just . groupBy') aggrs }+  where --- Grouping by an empty list is not the identity function!+        --- In fact it forms one single group.  Syntactically one+        --- cannot group by nothing in SQL, so we just group by a+        --- constant instead.  Because "GROUP BY 0" means group by the+        --- zeroth column, we instead use an expression rather than a+        --- constant.+        handleEmpty :: [HSql.SqlExpr] -> NEL.NonEmpty HSql.SqlExpr+        handleEmpty =+          M.fromMaybe (return (HSql.FunSqlExpr "COALESCE" [HSql.ConstSqlExpr "0"]))+          . NEL.nonEmpty++        groupBy' :: [(symbol, (Maybe aggrOp, HPQ.PrimExpr))]+                 -> NEL.NonEmpty HSql.SqlExpr+        groupBy' = (handleEmpty+                    . map sqlExpr                     . map expr-                    . filter (M.isNothing . aggrOp)) aggrs+                    . filter (M.isNothing . aggrOp))         attr = sqlBinding . Arr.second (uncurry aggrExpr)         expr (_, (_, e)) = e         aggrOp (_, (x, _)) = x@@ -113,30 +132,29 @@           PQ.OffsetOp n        -> (Nothing, Just n)           PQ.LimitOffsetOp l o -> (Just l, Just o) -join :: PQ.JoinType -> [(Symbol, HPQ.PrimExpr)] -> HPQ.PrimExpr -> Select -> Select-     -> Select-join j columns cond s1 s2 = SelectJoin Join { jJoinType = joinType j-                                            , jAttrs = mkAttrs columns-                                            , jTables = (s1, s2)-                                            , jCond = sqlExpr cond }-  where mkAttrs = map sqlBinding+join :: PQ.JoinType -> HPQ.PrimExpr -> Select -> Select -> Select+join j cond s1 s2 = SelectJoin Join { jJoinType = joinType j+                                    , jTables = (s1, s2)+                                    , jCond = sqlExpr cond }  -- Postgres seems to name columns of VALUES clauses "column1", -- "column2", ... . I'm not sure to what extent it is customisable or -- how robust it is to rely on this values :: [Symbol] -> [[HPQ.PrimExpr]] -> Select-values columns pes = SelectValues Values { vAttrs  = mkColumns columns+values columns pes = SelectValues Values { vAttrs  = SelectAttrs (mkColumns columns)                                          , vValues = (map . map) sqlExpr pes }-  where mkColumns = zipWith (flip (curry (sqlBinding . Arr.second mkColumn))) [1..]+  where mkColumns = ensureColumns . zipWith (flip (curry (sqlBinding . Arr.second mkColumn))) [1..]         mkColumn i = (HPQ.BaseTableAttrExpr . ("column" ++) . show) (i::Int)  binary :: PQ.BinOp -> [(Symbol, (HPQ.PrimExpr, HPQ.PrimExpr))]        -> (Select, Select) -> Select binary op pes (select1, select2) = SelectBinary Binary {   bOp = binOp op,-  bSelect1 = SelectFrom newSelect { attrs = map (mkColumn fst) pes,+  bSelect1 = SelectFrom newSelect { attrs = SelectAttrs+                                      (ensureColumns (map (mkColumn fst) pes)),                                     tables = [select1] },-  bSelect2 = SelectFrom newSelect { attrs = map (mkColumn snd) pes,+  bSelect2 = SelectFrom newSelect { attrs = SelectAttrs+                                      (ensureColumns (map (mkColumn snd) pes)),                                     tables = [select2] }   }   where mkColumn e = sqlBinding . Arr.second e@@ -152,10 +170,10 @@  newSelect :: From newSelect = From {-  attrs     = [],+  attrs     = Star,   tables    = [],   criteria  = [],-  groupBy   = [],+  groupBy   = Nothing,   orderBy   = [],   limit     = Nothing,   offset    = Nothing@@ -167,3 +185,8 @@ sqlBinding :: (Symbol, HPQ.PrimExpr) -> (HSql.SqlExpr, Maybe HSql.SqlColumn) sqlBinding (Symbol sym t, pe) =   (sqlExpr pe, Just (HSql.SqlColumn (T.tagWith t sym)))++ensureColumns :: [(HSql.SqlExpr, Maybe a)]+              -> NEL.NonEmpty (HSql.SqlExpr, Maybe a)+ensureColumns = M.fromMaybe (return (HSql.ConstSqlExpr "0", Nothing))+                . NEL.nonEmpty
src/Opaleye/Internal/Table.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE Rank2Types #-}  module Opaleye.Internal.Table where -import           Opaleye.Internal.Column (Column(Column))+import           Opaleye.Internal.Column (Column, unColumn) import qualified Opaleye.Internal.TableMaker as TM import qualified Opaleye.Internal.Tag as Tag import qualified Opaleye.Internal.PrimQuery as PQ@@ -10,10 +11,14 @@  import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ +import qualified Data.Functor.Identity as I import           Data.Profunctor (Profunctor, dimap, lmap) import           Data.Profunctor.Product (ProductProfunctor, empty, (***!)) import qualified Data.Profunctor.Product as PP+import qualified Data.List.NonEmpty as NEL+import           Data.Monoid (Monoid, mempty, mappend) import           Control.Applicative (Applicative, pure, (<*>), liftA2)+import qualified Control.Arrow as Arr  -- | Define a table as follows, where \"id\", \"color\", \"location\", -- \"quantity\" and \"radius\" are the tables columns in Postgres and@@ -48,14 +53,18 @@  data View columns = View columns --- If we switch to a more lens-like approach to PackMap this should be--- the equivalent of a Fold- -- There's no reason the second parameter should exist except that we -- use ProductProfunctors more than ProductContravariants so it makes -- things easier if we make it one of the former.-data Writer columns dummy =-  Writer (PM.PackMap (HPQ.PrimExpr, String) () columns ())+--+-- Writer has become very mysterious.  I really couldn't tell you what+-- it means.  It seems to be saying that a `Writer` tells you how an+-- `f columns` contains a list of `(f HPQ.PrimExpr, String)`, i.e. how+-- it contains each column: a column header and the entries in this+-- column for all the rows.+newtype Writer columns dummy =+  Writer (forall f. Functor f =>+          PM.PackMap (f HPQ.PrimExpr, String) () (f columns) ())  queryTable :: TM.ColumnMaker viewColumns columns             -> Table writerColumns viewColumns@@ -83,18 +92,36 @@  runWriter :: Writer columns columns' -> columns -> [(HPQ.PrimExpr, String)] runWriter (Writer (PM.PackMap f)) columns = outColumns-  where extractColumns t = ([t], ())-        (outColumns, ()) = f extractColumns columns+  where (outColumns, ()) = f extract (I.Identity columns)+        extract (pes, s) = ([(I.runIdentity pes, s)], ()) +-- This works more generally for any "zippable", that is an+-- Applicative that satisfies+--+--    x == (,) <$> fmap fst x <*> fmap snd x+--+-- However, I'm unaware of a typeclass for this.+runWriter' :: Writer columns columns' -> NEL.NonEmpty columns -> (NEL.NonEmpty [HPQ.PrimExpr], [String])+runWriter' (Writer (PM.PackMap f)) columns = Arr.first unZip outColumns+  where (outColumns, ()) = f extract columns+        extract (pes, s) = ((Zip (fmap return pes), [s]), ())++data Zip a = Zip { unZip :: NEL.NonEmpty [a] }++instance Monoid (Zip a) where+  mempty = Zip mempty'+    where mempty' = [] `NEL.cons` mempty'+  Zip xs `mappend` Zip ys = Zip (NEL.zipWith (++) xs ys)+ required :: String -> Writer (Column a) (Column a) required columnName =-  Writer (PM.PackMap (\f (Column primExpr) -> f (primExpr, columnName)))+  Writer (PM.PackMap (\f columns -> f (fmap unColumn columns, columnName)))  optional :: String -> Writer (Maybe (Column a)) (Column a) optional columnName =-  Writer (PM.PackMap (\f c -> case c of-                         Nothing -> pure ()-                         Just (Column primExpr) -> f (primExpr, columnName)))+  Writer (PM.PackMap (\f columns -> f (fmap maybeUnColumn columns, columnName)))+  where maybeUnColumn Nothing = HPQ.DefaultInsertExpr+        maybeUnColumn (Just column) = unColumn column  -- { @@ -108,7 +135,7 @@   Writer f <*> Writer x = Writer (liftA2 (\_ _ -> ()) f x)  instance Profunctor Writer where-  dimap f _ (Writer h) = Writer (lmap f h)+  dimap f _ (Writer h) = Writer (lmap (fmap f) h)  instance ProductProfunctor Writer where   empty = PP.defaultEmpty
src/Opaleye/Internal/TableMaker.hs view
@@ -26,13 +26,13 @@  runViewColumnMaker :: ViewColumnMaker strings tablecolumns ->                        strings -> tablecolumns-runViewColumnMaker (ViewColumnMaker f) = PM.over f id+runViewColumnMaker (ViewColumnMaker f) = PM.overPM f id  runColumnMaker :: Applicative f                   => ColumnMaker tablecolumns columns                   -> (HPQ.PrimExpr -> f HPQ.PrimExpr)                   -> tablecolumns -> f columns-runColumnMaker (ColumnMaker f) = PM.packmap f+runColumnMaker (ColumnMaker f) = PM.traversePM f  -- There's surely a way of simplifying this implementation tableColumn :: ViewColumnMaker String (C.Column a)
src/Opaleye/Internal/Tag.hs view
@@ -1,6 +1,6 @@ module Opaleye.Internal.Tag where -data Tag = UnsafeTag Int deriving (Read, Show)+newtype Tag = UnsafeTag Int deriving (Read, Show)  start :: Tag start = UnsafeTag 1
src/Opaleye/Internal/Unpackspec.hs view
@@ -15,18 +15,43 @@ import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ  newtype Unpackspec columns columns' =+  -- | An 'Unpackspec' @columns@ @columns'@ allows you to extract and+  -- modify a sequence of 'HPQ.PrimExpr's inside a value of type+  -- @columns@.+  --+  -- For example, the 'Default' instance of type 'Unpackspec' @(Column+  -- a, Column b)@ @(Column a, Column b)@ allows you to manipulate or+  -- extract the two 'HPQ.PrimExpr's inside a @(Column a, Column b)@.  The+  -- 'Default' instance of type @Foo (Column a) (Column b) (Column c)@+  -- will allow you to manipulate or extract the three 'HPQ.PrimExpr's+  -- contained therein (for a user-defined product type @Foo@, assuming+  -- the @makeAdaptorAndInstance@ splice from+  -- @Data.Profunctor.Product.TH@ has been run).+  --+  -- You can create 'Unpackspec's by hand using 'unpackspecColumn' and+  -- the 'Profunctor', 'ProductProfunctor' and 'SumProfunctor'+  -- operations.  However, in practice users should almost never need+  -- to create or manipulate them.  Typically they will be created+  -- automatically by the 'D.Default' instance.   Unpackspec (PM.PackMap HPQ.PrimExpr HPQ.PrimExpr columns columns') +-- | Target the single 'HPQ.PrimExpr' inside a 'C.Column' unpackspecColumn :: Unpackspec (C.Column a) (C.Column a) unpackspecColumn = Unpackspec                    (PM.PackMap (\f (IC.Column pe) -> fmap IC.Column (f pe))) +-- | Modify all the targeted 'HPQ.PrimExpr's runUnpackspec :: Applicative f                  => Unpackspec columns b                  -> (HPQ.PrimExpr -> f HPQ.PrimExpr)                  -> columns -> f b-runUnpackspec (Unpackspec f) = PM.packmap f+runUnpackspec (Unpackspec f) = PM.traversePM f +-- | Extract all the targeted 'HPQ.PrimExpr's+collectPEs :: Unpackspec s t -> s -> [HPQ.PrimExpr]+collectPEs unpackspec = fst . runUnpackspec unpackspec f+  where f pe = ([pe], pe)+ instance D.Default Unpackspec (C.Column a) (C.Column a) where   def = unpackspecColumn @@ -47,5 +72,8 @@ instance ProductProfunctor Unpackspec where   empty = PP.defaultEmpty   (***!) = PP.defaultProfunctorProduct++instance PP.SumProfunctor Unpackspec where+  Unpackspec x1 +++! Unpackspec x2 = Unpackspec (x1 PP.+++! x2)  --}
src/Opaleye/Internal/Values.hs view
@@ -68,12 +68,12 @@                    -> PM.PM [(HPQ.Symbol, HPQ.PrimExpr)] HPQ.PrimExpr extractValuesField = PM.extractAttr "values" -data Valuesspec columns columns' =+newtype Valuesspec columns columns' =   Valuesspec (PM.PackMap HPQ.PrimExpr HPQ.PrimExpr () columns')  runValuesspec :: Applicative f => Valuesspec columns columns'               -> (HPQ.PrimExpr -> f HPQ.PrimExpr) -> f columns'-runValuesspec (Valuesspec v) f = PM.packmap v f ()+runValuesspec (Valuesspec v) f = PM.traversePM v f ()  instance Default Valuesspec (Column T.PGInt4) (Column T.PGInt4) where   def = Valuesspec (PM.PackMap (\f () -> fmap Column (f (HPQ.ConstExpr HPQ.NullLit))))
src/Opaleye/Join.hs view
@@ -6,7 +6,6 @@ import qualified Opaleye.Internal.Join as J import qualified Opaleye.Internal.Tag as T import qualified Opaleye.Internal.PrimQuery as PQ-import qualified Opaleye.Internal.PackMap as PM import           Opaleye.QueryArr (Query) import qualified Opaleye.Internal.QueryArr as Q import           Opaleye.Internal.Column (Column(Column))@@ -35,23 +34,21 @@           -> Query (columnsA, nullableColumnsB) leftJoin = leftJoinExplicit D.def D.def D.def +-- We don't actually need the Unpackspecs any more, but I'm going to+-- leave them here in case they're ever needed again.  I don't want to+-- have to break the API to add them back. leftJoinExplicit :: U.Unpackspec columnsA columnsA                  -> U.Unpackspec columnsB columnsB                  -> J.NullMaker columnsB nullableColumnsB                  -> Query columnsA -> Query columnsB                  -> ((columnsA, columnsB) -> Column T.PGBool)                  -> Query (columnsA, nullableColumnsB)-leftJoinExplicit unpackA unpackB nullmaker qA qB cond = Q.simpleQueryArr q where-  q ((), startTag) = ((newColumnsA, nullableColumnsB), primQueryR, T.next endTag)+leftJoinExplicit _ _ nullmaker qA qB cond = Q.simpleQueryArr q where+  q ((), startTag) = ((columnsA, nullableColumnsB), primQueryR, T.next endTag)     where (columnsA, primQueryA, midTag) = Q.runSimpleQueryArr qA ((), startTag)           (columnsB, primQueryB, endTag) = Q.runSimpleQueryArr qB ((), midTag) -          (newColumnsA, ljPEsA) =-            PM.run (U.runUnpackspec unpackA (J.extractLeftJoinFields 1 endTag) columnsA)-          (newColumnsB, ljPEsB) =-            PM.run (U.runUnpackspec unpackB (J.extractLeftJoinFields 2 endTag) columnsB)--          nullableColumnsB = J.toNullable nullmaker newColumnsB+          nullableColumnsB = J.toNullable nullmaker columnsB            Column cond' = cond (columnsA, columnsB)-          primQueryR = PQ.Join PQ.LeftJoin (ljPEsA ++ ljPEsB) cond' primQueryA primQueryB+          primQueryR = PQ.Join PQ.LeftJoin cond' primQueryA primQueryB
src/Opaleye/Manipulation.hs view
@@ -10,11 +10,10 @@ import qualified Opaleye.Table as T import qualified Opaleye.Internal.Table as TI import           Opaleye.Internal.Column (Column(Column))-import           Opaleye.Internal.Helpers ((.:), (.:.), (.::))+import           Opaleye.Internal.Helpers ((.:), (.:.), (.::), (.::.)) import qualified Opaleye.Internal.Unpackspec as U import           Opaleye.PGTypes (PGBool) -import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ import qualified Opaleye.Internal.HaskellDB.Sql as HSql import qualified Opaleye.Internal.HaskellDB.Sql.Print as HPrint import qualified Opaleye.Internal.HaskellDB.Sql.Default as SD@@ -26,12 +25,10 @@  import           Data.Int (Int64) import           Data.String (fromString)+import qualified Data.List.NonEmpty as NEL  arrangeInsert :: T.Table columns a -> columns -> HSql.SqlInsert-arrangeInsert (T.Table tableName (TI.TableProperties writer _)) columns = insert-  where outColumns' = (map (\(x, y) -> (y, x))-                       . TI.runWriter writer) columns-        insert = SG.sqlInsert SD.defaultSqlGenerator tableName outColumns'+arrangeInsert t c = arrangeInsertMany t (return c)  arrangeInsertSql :: T.Table columns a -> columns -> String arrangeInsertSql = show . HPrint.ppInsert .: arrangeInsert@@ -39,6 +36,24 @@ runInsert :: PGS.Connection -> T.Table columns columns' -> columns -> IO Int64 runInsert conn = PGS.execute_ conn . fromString .: arrangeInsertSql +arrangeInsertMany :: T.Table columns a -> NEL.NonEmpty columns -> HSql.SqlInsert+arrangeInsertMany (T.Table tableName (TI.TableProperties writer _)) columns = insert+  where (columnExprs, columnNames) = TI.runWriter' writer columns+        insert = SG.sqlInsert SD.defaultSqlGenerator+                      tableName columnNames columnExprs++arrangeInsertManySql :: T.Table columns a -> NEL.NonEmpty columns -> String+arrangeInsertManySql = show . HPrint.ppInsert .: arrangeInsertMany++runInsertMany :: PGS.Connection+              -> T.Table columns columns'+              -> [columns]+              -> IO Int64+runInsertMany conn table columns = case NEL.nonEmpty columns of+  -- Inserting the empty list is just the same as returning 0+  Nothing       -> return 0+  Just columns' -> (PGS.execute_ conn . fromString .: arrangeInsertManySql) table columns'+ arrangeUpdate :: T.Table columnsW columnsR               -> (columnsR -> columnsW) -> (columnsR -> Column PGBool)               -> HSql.SqlUpdate@@ -73,7 +88,7 @@           -> IO Int64 runDelete conn = PGS.execute_ conn . fromString .: arrangeDeleteSql -arrangeInsertReturning :: U.Unpackspec returned returned+arrangeInsertReturning :: U.Unpackspec returned ignored                        -> T.Table columnsW columnsR                        -> columnsW                        -> (columnsR -> returned)@@ -82,14 +97,10 @@   Sql.Returning insert returningSEs   where insert = arrangeInsert table columns         TI.Table _ (TI.TableProperties _ (TI.View columnsR)) = table-        returning = returningf columnsR-        -- TODO: duplication with runQueryArrUnpack-        f pe = ([pe], pe)-        returningPEs :: [HPQ.PrimExpr]-        (returningPEs, _) = U.runUnpackspec unpackspec f returning+        returningPEs = U.collectPEs unpackspec (returningf columnsR)         returningSEs = map Sql.sqlExpr returningPEs -arrangeInsertReturningSql :: U.Unpackspec returned returned+arrangeInsertReturningSql :: U.Unpackspec returned ignored                           -> T.Table columnsW columnsR                           -> columnsW                           -> (columnsR -> returned)@@ -99,26 +110,74 @@                             .:: arrangeInsertReturning  runInsertReturningExplicit :: RQ.QueryRunner returned haskells-                           -> U.Unpackspec returned returned-                           -> PGS.Connection-                           -> T.Table columnsW columnsR-                           -> columnsW-                           -> (columnsR -> returned)-                           -> IO [haskells]-runInsertReturningExplicit qr u conn = PGS.queryWith_ rowParser conn-                                       . fromString-                                       .:. arrangeInsertReturningSql u-  where IRQ.QueryRunner _ rowParser = qr+                            -> PGS.Connection+                            -> T.Table columnsW columnsR+                            -> columnsW+                            -> (columnsR -> returned)+                            -> IO [haskells]+runInsertReturningExplicit qr conn t w r = PGS.queryWith_ (rowParser (r v)) conn+                                             (fromString+                                             (arrangeInsertReturningSql u t w r))+  where IRQ.QueryRunner u rowParser _ = qr+        --- ^^ TODO: need to make sure we're not trying to read zero rows+        TI.Table _ (TI.TableProperties _ (TI.View v)) = t+        -- This method of getting hold of the return type feels a bit+        -- suspect.  I haven't checked it for validity.  -- | @runInsertReturning@'s use of the 'D.Default' typeclass means that the -- compiler will have trouble inferring types.  It is strongly -- recommended that you provide full type signatures when using -- @runInsertReturning@.-runInsertReturning :: (D.Default RQ.QueryRunner returned haskells,-                       D.Default U.Unpackspec returned returned)+runInsertReturning :: (D.Default RQ.QueryRunner returned haskells)                       => PGS.Connection                       -> T.Table columnsW columnsR                       -> columnsW                       -> (columnsR -> returned)                       -> IO [haskells]-runInsertReturning = runInsertReturningExplicit D.def D.def+runInsertReturning = runInsertReturningExplicit D.def++arrangeUpdateReturning :: U.Unpackspec returned ignored+                       -> T.Table columnsW columnsR+                       -> (columnsR -> columnsW)+                       -> (columnsR -> Column PGBool)+                       -> (columnsR -> returned)+                       -> Sql.Returning HSql.SqlUpdate+arrangeUpdateReturning unpackspec table updatef cond returningf =+  Sql.Returning update returningSEs+  where update = arrangeUpdate table updatef cond+        TI.Table _ (TI.TableProperties _ (TI.View columnsR)) = table+        returningPEs = U.collectPEs unpackspec (returningf columnsR)+        returningSEs = map Sql.sqlExpr returningPEs++arrangeUpdateReturningSql :: U.Unpackspec returned ignored+                       -> T.Table columnsW columnsR+                       -> (columnsR -> columnsW)+                       -> (columnsR -> Column PGBool)+                       -> (columnsR -> returned)+                       -> String+arrangeUpdateReturningSql = show+                            . Print.ppUpdateReturning+                            .::. arrangeUpdateReturning++runUpdateReturningExplicit :: RQ.QueryRunner returned haskells+                           -> PGS.Connection+                           -> T.Table columnsW columnsR+                           -> (columnsR -> columnsW)+                           -> (columnsR -> Column PGBool)+                           -> (columnsR -> returned)+                           -> IO [haskells]+runUpdateReturningExplicit qr conn t update cond r =+  PGS.queryWith_ (rowParser (r v)) conn+                 (fromString (arrangeUpdateReturningSql u t update cond r))+  where IRQ.QueryRunner u rowParser _ = qr+        --- ^^ TODO: need to make sure we're not trying to read zero rows+        TI.Table _ (TI.TableProperties _ (TI.View v)) = t++runUpdateReturning :: (D.Default RQ.QueryRunner returned haskells)+                      => PGS.Connection+                      -> T.Table columnsW columnsR+                      -> (columnsR -> columnsW)+                      -> (columnsR -> Column PGBool)+                      -> (columnsR -> returned)+                      -> IO [haskells]+runUpdateReturning = runUpdateReturningExplicit D.def
src/Opaleye/Operators.hs view
@@ -1,10 +1,13 @@ module Opaleye.Operators (module Opaleye.Operators) where +import qualified Data.Foldable as F+ import           Opaleye.Internal.Column (Column(Column), unsafeCase_,                                           unsafeIfThenElse, unsafeGt, unsafeEq) import qualified Opaleye.Internal.Column as C import           Opaleye.Internal.QueryArr (QueryArr(QueryArr)) import qualified Opaleye.Internal.PrimQuery as PQ+import qualified Opaleye.Order as Ord import qualified Opaleye.PGTypes as T  import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ@@ -28,19 +31,19 @@ (./=) = C.binOp HPQ.OpNotEq  infix 4 .>-(.>) :: Column a -> Column a -> Column T.PGBool+(.>) :: Ord.PGOrd a => Column a -> Column a -> Column T.PGBool (.>) = unsafeGt  infix 4 .<-(.<) :: Column a -> Column a -> Column T.PGBool+(.<) :: Ord.PGOrd a => Column a -> Column a -> Column T.PGBool (.<) = C.binOp HPQ.OpLt  infix 4 .<=-(.<=) :: Column a -> Column a -> Column T.PGBool+(.<=) :: Ord.PGOrd a => Column a -> Column a -> Column T.PGBool (.<=) = C.binOp HPQ.OpLtEq  infix 4 .>=-(.>=) :: Column a -> Column a -> Column T.PGBool+(.>=) :: Ord.PGOrd a => Column a -> Column a -> Column T.PGBool (.>=) = C.binOp HPQ.OpGtEq  case_ :: [(Column T.PGBool, Column a)] -> Column a -> Column a@@ -71,3 +74,9 @@  like :: Column T.PGText -> Column T.PGText -> Column T.PGBool like = C.binOp HPQ.OpLike++ors :: F.Foldable f => f (Column T.PGBool) -> Column T.PGBool+ors = F.foldl' (.||) (T.pgBool False)++in_ :: (Functor f, F.Foldable f) => f (Column a) -> Column a -> Column T.PGBool+in_ hs w = ors . fmap (w .==) $ hs
src/Opaleye/Order.hs view
@@ -4,22 +4,52 @@ import           Opaleye.QueryArr (Query) import qualified Opaleye.Internal.QueryArr as Q import qualified Opaleye.Internal.Order as O+import qualified Opaleye.PGTypes as T  import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ -{-| Order the rows of a `Query` according to the `Order`. -}+{-| Order the rows of a `Query` according to the `Order`.++@+import Data.Monoid (\<\>)++\-- Order by the first column ascending.  When first columns are equal+\-- order by second column descending.+example :: 'Query' ('C.Column' 'T.PGInt4', 'C.Column' 'T.PGText')+        -> 'Query' ('C.Column' 'T.PGInt4', 'C.Column' 'T.PGText')+example = 'orderBy' ('asc' fst \<\> 'desc' snd)+@++-} orderBy :: O.Order a -> Query a -> Query a orderBy os q =   Q.simpleQueryArr (O.orderByU os . Q.runSimpleQueryArr q)  -- | Specify an ascending ordering by the given expression.-asc :: (a -> C.Column b) -> O.Order a-asc = O.order HPQ.OpAsc+--   (Any NULLs appear last)+asc :: PGOrd b => (a -> C.Column b) -> O.Order a+asc = O.order HPQ.OrderOp { HPQ.orderDirection = HPQ.OpAsc+                          , HPQ.orderNulls     = HPQ.NullsLast }  -- | Specify an descending ordering by the given expression.-desc :: (a -> C.Column b) -> O.Order a-desc = O.order HPQ.OpDesc+--   (Any NULLs appear first)+desc :: PGOrd b => (a -> C.Column b) -> O.Order a+desc = O.order HPQ.OrderOp { HPQ.orderDirection = HPQ.OpDesc+                           , HPQ.orderNulls     = HPQ.NullsFirst } +-- | Specify an ascending ordering by the given expression.+--   (Any NULLs appear first)+ascNullsFirst :: PGOrd b => (a -> C.Column b) -> O.Order a+ascNullsFirst = O.order HPQ.OrderOp { HPQ.orderDirection = HPQ.OpAsc+                                    , HPQ.orderNulls     = HPQ.NullsFirst }+++-- | Specify an descending ordering by the given expression.+--   (Any NULLs appear last)+descNullsLast :: PGOrd b => (a -> C.Column b) -> O.Order a+descNullsLast = O.order HPQ.OrderOp { HPQ.orderDirection = HPQ.OpDesc+                                    , HPQ.orderNulls     = HPQ.NullsLast }+ {- | Limit the results of the given query to the given maximum number of items.@@ -33,3 +63,20 @@ -} offset :: Int -> Query a -> Query a offset n a = Q.simpleQueryArr (O.offset' n . Q.runSimpleQueryArr a)++-- | Typeclass for Postgres types which support ordering operations.+class PGOrd a where++instance PGOrd T.PGBool+instance PGOrd T.PGDate+instance PGOrd T.PGFloat8+instance PGOrd T.PGFloat4+instance PGOrd T.PGInt8+instance PGOrd T.PGInt4+instance PGOrd T.PGInt2+instance PGOrd T.PGNumeric+instance PGOrd T.PGText+instance PGOrd T.PGTime+instance PGOrd T.PGTimestamptz+instance PGOrd T.PGTimestamp+instance PGOrd T.PGCitext
src/Opaleye/PGTypes.hs view
@@ -1,11 +1,13 @@ {-# LANGUAGE EmptyDataDecls #-} -module Opaleye.PGTypes where+module Opaleye.PGTypes (module Opaleye.PGTypes) where -import           Opaleye.Internal.Column (Column(Column))+import           Opaleye.Internal.Column (Column) import qualified Opaleye.Internal.Column as C+import qualified Opaleye.Internal.PGTypes as IPT  import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ+import qualified Opaleye.Internal.HaskellDB.Sql.Default as HSD (quote)  import qualified Data.CaseInsensitive as CI import qualified Data.Text as SText@@ -13,7 +15,6 @@ import qualified Data.ByteString as SByteString import qualified Data.ByteString.Lazy as LByteString import qualified Data.Time as Time-import qualified Data.Time.Locale.Compat as Locale import qualified Data.UUID as UUID  import           Data.Int (Int64)@@ -34,6 +35,8 @@ data PGCitext data PGArray a data PGBytea+data PGJson+data PGJsonb  instance C.PGNum PGFloat8 where   pgFromInteger = pgDouble . fromInteger@@ -48,67 +51,91 @@   pgFromRational = pgDouble . fromRational  literalColumn :: HPQ.Literal -> Column a-literalColumn = Column . HPQ.ConstExpr+literalColumn = IPT.literalColumn+{-# WARNING literalColumn+    "'literalColumn' has been moved to Opaleye.Internal.PGTypes"+  #-}  pgString :: String -> Column PGText-pgString = literalColumn . HPQ.StringLit+pgString = IPT.literalColumn . HPQ.StringLit  pgLazyByteString :: LByteString.ByteString -> Column PGBytea-pgLazyByteString = literalColumn . HPQ.ByteStringLit . LByteString.toStrict+pgLazyByteString = IPT.literalColumn . HPQ.ByteStringLit . LByteString.toStrict  pgStrictByteString :: SByteString.ByteString -> Column PGBytea-pgStrictByteString = literalColumn . HPQ.ByteStringLit+pgStrictByteString = IPT.literalColumn . HPQ.ByteStringLit  pgStrictText :: SText.Text -> Column PGText-pgStrictText = literalColumn . HPQ.StringLit . SText.unpack+pgStrictText = IPT.literalColumn . HPQ.StringLit . SText.unpack  pgLazyText :: LText.Text -> Column PGText-pgLazyText = literalColumn . HPQ.StringLit . LText.unpack+pgLazyText = IPT.literalColumn . HPQ.StringLit . LText.unpack  pgInt4 :: Int -> Column PGInt4-pgInt4 = literalColumn . HPQ.IntegerLit . fromIntegral+pgInt4 = IPT.literalColumn . HPQ.IntegerLit . fromIntegral  pgInt8 :: Int64 -> Column PGInt8-pgInt8 = literalColumn . HPQ.IntegerLit . fromIntegral+pgInt8 = IPT.literalColumn . HPQ.IntegerLit . fromIntegral  pgDouble :: Double -> Column PGFloat8-pgDouble = literalColumn . HPQ.DoubleLit+pgDouble = IPT.literalColumn . HPQ.DoubleLit  pgBool :: Bool -> Column PGBool-pgBool = literalColumn . HPQ.BoolLit+pgBool = IPT.literalColumn . HPQ.BoolLit  pgUUID :: UUID.UUID -> Column PGUuid-pgUUID = C.unsafeCoerce . pgString . UUID.toString+pgUUID = C.unsafeCoerceColumn . pgString . UUID.toString --- Internal use only! unsafePgFormatTime :: Time.FormatTime t => HPQ.Name -> String -> t -> Column c-unsafePgFormatTime typeName formatString = Column-                                     . HPQ.CastExpr typeName-                                     . HPQ.ConstExpr-                                     . HPQ.OtherLit-                                     . format-  where format = Time.formatTime Locale.defaultTimeLocale formatString+unsafePgFormatTime = IPT.unsafePgFormatTime+{-# WARNING unsafePgFormatTime+    "'unsafePgFormatTime' has been moved to Opaleye.Internal.PGTypes"+  #-}  pgDay :: Time.Day -> Column PGDate-pgDay = unsafePgFormatTime "date" "'%F'"+pgDay = IPT.unsafePgFormatTime "date" "'%F'"  pgUTCTime :: Time.UTCTime -> Column PGTimestamptz-pgUTCTime = unsafePgFormatTime "timestamptz" "'%FT%TZ'"+pgUTCTime = IPT.unsafePgFormatTime "timestamptz" "'%FT%TZ'"  pgLocalTime :: Time.LocalTime -> Column PGTimestamp-pgLocalTime = unsafePgFormatTime "timestamp" "'%FT%T'"+pgLocalTime = IPT.unsafePgFormatTime "timestamp" "'%FT%T'"  pgTimeOfDay :: Time.TimeOfDay -> Column PGTime-pgTimeOfDay = unsafePgFormatTime "time" "'%T'"+pgTimeOfDay = IPT.unsafePgFormatTime "time" "'%T'"  -- "We recommend not using the type time with time zone" -- http://www.postgresql.org/docs/8.3/static/datatype-datetime.html   pgCiStrictText :: CI.CI SText.Text -> Column PGCitext-pgCiStrictText = literalColumn . HPQ.StringLit . SText.unpack . CI.original+pgCiStrictText = IPT.literalColumn . HPQ.StringLit . SText.unpack . CI.original  pgCiLazyText :: CI.CI LText.Text -> Column PGCitext-pgCiLazyText = literalColumn . HPQ.StringLit . LText.unpack . CI.original+pgCiLazyText = IPT.literalColumn . HPQ.StringLit . LText.unpack . CI.original  -- No CI String instance since postgresql-simple doesn't define FromField (CI String)++-- The json data type was introduced in PostgreSQL version 9.2+-- JSON values must be SQL string quoted+pgJSON :: String -> Column PGJson+pgJSON = IPT.castToType "json" . HSD.quote++pgStrictJSON :: SByteString.ByteString -> Column PGJson+pgStrictJSON = pgJSON . IPT.strictDecodeUtf8++pgLazyJSON :: LByteString.ByteString -> Column PGJson+pgLazyJSON = pgJSON . IPT.lazyDecodeUtf8++-- The jsonb data type was introduced in PostgreSQL version 9.4+-- JSONB values must be SQL string quoted+--+-- TODO: We need to add literal JSON and JSONB types.+pgJSONB :: String -> Column PGJsonb+pgJSONB = IPT.castToType "jsonb" . HSD.quote++pgStrictJSONB :: SByteString.ByteString -> Column PGJsonb+pgStrictJSONB = pgJSONB . IPT.strictDecodeUtf8++pgLazyJSONB :: LByteString.ByteString -> Column PGJsonb+pgLazyJSONB = pgJSONB . IPT.lazyDecodeUtf8
src/Opaleye/RunQuery.hs view
@@ -3,9 +3,11 @@ module Opaleye.RunQuery (module Opaleye.RunQuery,                          QueryRunner,                          IRQ.QueryRunnerColumn,+                         IRQ.QueryRunnerColumnDefault (..),                          IRQ.fieldQueryRunnerColumn) where  import qualified Database.PostgreSQL.Simple as PGS+import qualified Database.PostgreSQL.Simple.FromRow as FR import qualified Data.String as String  import           Opaleye.Column (Column)@@ -13,10 +15,13 @@ import           Opaleye.QueryArr (Query) import           Opaleye.Internal.RunQuery (QueryRunner(QueryRunner)) import qualified Opaleye.Internal.RunQuery as IRQ+import qualified Opaleye.Internal.QueryArr as Q  import qualified Data.Profunctor as P import qualified Data.Profunctor.Product.Default as D +import           Control.Applicative ((*>))+ -- | @runQuery@'s use of the 'D.Default' typeclass means that the -- compiler will have trouble inferring types.  It is strongly -- recommended that you provide full type signatures when using@@ -47,10 +52,20 @@                  -> PGS.Connection                  -> Query columns                  -> IO [haskells]-runQueryExplicit (QueryRunner u rowParser) conn q =-  PGS.queryWith_ rowParser conn sql+runQueryExplicit (QueryRunner u rowParser nonZeroColumns) conn q =+  PGS.queryWith_ parser conn sql   where sql :: PGS.Query         sql = String.fromString (S.showSqlForPostgresExplicit u q)+        -- FIXME: We're doing work twice here+        (b, _, _) = Q.runSimpleQueryArrStart q ()+        parser = if nonZeroColumns b+                 then rowParser b+                 else (FR.fromRow :: FR.RowParser (PGS.Only Int)) *> rowParser b+                 -- If we are selecting zero columns then the SQL+                 -- generator will have to put a dummy 0 into the+                 -- SELECT statement, since we can't select zero+                 -- columns.  In that case we have to make sure we+                 -- read a single Int.  -- | Use 'queryRunnerColumn' to make an instance to allow you to run queries on --   your own datatypes.  For example:
src/Opaleye/Table.hs view
@@ -9,14 +9,12 @@ import           Opaleye.Internal.Column (Column(Column)) import qualified Opaleye.Internal.QueryArr as Q import qualified Opaleye.Internal.Table as T-import           Opaleye.Internal.Table (View(View), Writer(Writer),-                                         Table, TableProperties)+import           Opaleye.Internal.Table (View(View), Table, Writer,+                                         TableProperties) import qualified Opaleye.Internal.TableMaker as TM import qualified Opaleye.Internal.Tag as Tag-import qualified Opaleye.Internal.PackMap as PM  import qualified Data.Profunctor.Product.Default as D-import           Control.Applicative (Applicative, pure)  import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ @@ -44,12 +42,10 @@  required :: String -> TableProperties (Column a) (Column a) required columnName = T.TableProperties-  (Writer (PM.PackMap (\f (Column primExpr) -> f (primExpr, columnName))))+  (T.required columnName)   (View (Column (HPQ.BaseTableAttrExpr columnName)))  optional :: String -> TableProperties (Maybe (Column a)) (Column a) optional columnName = T.TableProperties-  (Writer (PM.PackMap (\f c -> case c of-                          Nothing -> pure ()-                          Just (Column primExpr) -> f (primExpr, columnName))))+  (T.optional columnName)   (View (Column (HPQ.BaseTableAttrExpr columnName)))