{"action":"created","issue":{"url":"https://api.github.com/repos/tomjaguarpaw/haskell-opaleye/issues/99","labels_url":"https://api.github.com/repos/tomjaguarpaw/haskell-opaleye/issues/99/labels{/name}","comments_url":"https://api.github.com/repos/tomjaguarpaw/haskell-opaleye/issues/99/comments","events_url":"https://api.github.com/repos/tomjaguarpaw/haskell-opaleye/issues/99/events","html_url":"https://github.com/tomjaguarpaw/haskell-opaleye/pull/99","id":101995064,"number":99,"title":"Composite Types (Issue #98)","user":{"login":"WraithM","id":1512913,"avatar_url":"https://avatars.githubusercontent.com/u/1512913?v=3","gravatar_id":"","url":"https://api.github.com/users/WraithM","html_url":"https://github.com/WraithM","followers_url":"https://api.github.com/users/WraithM/followers","following_url":"https://api.github.com/users/WraithM/following{/other_user}","gists_url":"https://api.github.com/users/WraithM/gists{/gist_id}","starred_url":"https://api.github.com/users/WraithM/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/WraithM/subscriptions","organizations_url":"https://api.github.com/users/WraithM/orgs","repos_url":"https://api.github.com/users/WraithM/repos","events_url":"https://api.github.com/users/WraithM/events{/privacy}","received_events_url":"https://api.github.com/users/WraithM/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":38,"created_at":"2015-08-19T20:46:07Z","updated_at":"2015-08-20T20:50:46Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/tomjaguarpaw/haskell-opaleye/pulls/99","html_url":"https://github.com/tomjaguarpaw/haskell-opaleye/pull/99","diff_url":"https://github.com/tomjaguarpaw/haskell-opaleye/pull/99.diff","patch_url":"https://github.com/tomjaguarpaw/haskell-opaleye/pull/99.patch"},"body":"Adding composite types by creating a new data type Composite, which plays a similar role to Column. Created a new expression constructor for HaskellDB's PrimQuery, and modified HaskellDB's SqlColumn type.\r\n\r\nHere's an example of using it:\r\n```sql\r\ncreate type location as (\r\n    x double precision,\r\n    y double precision\r\n);\r\n\r\ncreate table locations (\r\n    id serial primary key,\r\n    location1 location,\r\n    location2 location\r\n);\r\n```\r\n\r\n```haskell\r\n{-# LANGUAGE EmptyDataDecls        #-}\r\n{-# LANGUAGE FlexibleInstances     #-}\r\n{-# LANGUAGE MultiParamTypeClasses #-}\r\n{-# LANGUAGE OverloadedStrings     #-}\r\n{-# LANGUAGE TemplateHaskell       #-}\r\n\r\nmodule Main where\r\n\r\nimport           Data.Maybe                           (listToMaybe)\r\nimport           Data.Profunctor.Product.Default      as D\r\nimport           Data.Profunctor.Product.TH           (makeAdaptorAndInstance)\r\n\r\nimport qualified Database.PostgreSQL.Simple           as PSQL\r\nimport           Database.PostgreSQL.Simple.FromRow\r\n\r\nimport           Opaleye                              hiding (literalColumn)\r\nimport           Opaleye.Internal.Column\r\nimport           Opaleye.Internal.HaskellDB.PrimQuery as HPQ\r\nimport           Opaleye.Internal.PGTypes\r\nimport           Opaleye.Internal.RunQuery\r\n\r\ndata Location = Location\r\n    { x :: Double\r\n    , y :: Double\r\n    } deriving (Show, Eq)\r\n\r\ndata PGLocation\r\n\r\npgLocation :: Location -> Composite PGLocation\r\npgLocation (Location x y) = WriteComposite . HPQ.ConstExpr . HPQ.OtherLit $\r\n    \"(\" ++ show x ++ \"::double precision,\" ++ show y ++ \"::double precision)\"\r\n\r\ninstance FromRow Location where\r\n    fromRow = Location <$> field <*> field\r\n\r\ninstance D.Default QueryRunner (Composite PGLocation) Location where\r\n    def = rowQueryRunner\r\n\r\ndata Locations = Locations\r\n    { l1 :: Location\r\n    , l2 :: Location\r\n    } deriving (Show, Eq)\r\n\r\ndata LocationsP i a b = LocationsP\r\n    { pId :: i\r\n    , pl1 :: a\r\n    , pl2 :: b\r\n    } deriving (Show, Eq)\r\n\r\nmakeAdaptorAndInstance \"pLocations\" ''LocationsP\r\n\r\ntype WriteLocations = LocationsP\r\n    (Maybe (Column PGInt4))\r\n    (Composite PGLocation)\r\n    (Composite PGLocation)\r\n\r\ntype ReadLocations = LocationsP\r\n    (Column PGInt4)\r\n    (Composite PGLocation)\r\n    (Composite PGLocation)\r\n\r\nlocationsTable :: Table WriteLocations ReadLocations\r\nlocationsTable = Table \"locations\"\r\n    (pLocations LocationsP\r\n        { pId = optional \"id\"\r\n        , pl1 = compositeRequired \"location1\" [\"x\", \"y\"]\r\n        , pl2 = compositeRequired \"location2\" [\"x\", \"y\"]\r\n        })\r\n\r\nselectAllLocations :: Query ReadLocations\r\nselectAllLocations = queryTable locationsTable\r\n\r\nrunLocationsQuery :: PSQL.Connection -> Query ReadLocations -> IO [(Int, Locations)]\r\nrunLocationsQuery c q = map mkLocations <$> runQuery c q\r\n  where\r\n    mkLocations :: LocationsP Int Location Location -> (Int, Locations)\r\n    mkLocations (LocationsP i p1 p2) = (i, Locations p1 p2)\r\n\r\ninsertLocations :: PSQL.Connection -> Locations -> IO (Maybe Int)\r\ninsertLocations c (Locations p1 p2) =\r\n    listToMaybe <$> runInsertReturning c locationsTable wlocations pId\r\n  where\r\n    wlocations = LocationsP Nothing (pgLocation p1) (pgLocation p2)\r\n\r\nmain :: IO ()\r\nmain = do\r\n    c <- PSQL.connectPostgreSQL \"dbname='testpoints' user='postgres'\"\r\n\r\n    printSql selectAllLocations\r\n\r\n    _ <- insertLocations c (Locations (Location 1.2 2.1) (Location 2.3 62.1))\r\n    _ <- insertLocations c (Locations (Location 3.2 6.1) (Location 2 51.1))\r\n    runLocationsQuery c selectAllLocations >>= printLines\r\n  where\r\n    printSql = putStrLn . showSqlForPostgres\r\n    printLines = putStrLn . unlines . map show\r\n```"},"comment":{"url":"https://api.github.com/repos/tomjaguarpaw/haskell-opaleye/issues/comments/133167090","html_url":"https://github.com/tomjaguarpaw/haskell-opaleye/pull/99#issuecomment-133167090","issue_url":"https://api.github.com/repos/tomjaguarpaw/haskell-opaleye/issues/99","id":133167090,"user":{"login":"WraithM","id":1512913,"avatar_url":"https://avatars.githubusercontent.com/u/1512913?v=3","gravatar_id":"","url":"https://api.github.com/users/WraithM","html_url":"https://github.com/WraithM","followers_url":"https://api.github.com/users/WraithM/followers","following_url":"https://api.github.com/users/WraithM/following{/other_user}","gists_url":"https://api.github.com/users/WraithM/gists{/gist_id}","starred_url":"https://api.github.com/users/WraithM/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/WraithM/subscriptions","organizations_url":"https://api.github.com/users/WraithM/orgs","repos_url":"https://api.github.com/users/WraithM/repos","events_url":"https://api.github.com/users/WraithM/events{/privacy}","received_events_url":"https://api.github.com/users/WraithM/received_events","type":"User","site_admin":false},"created_at":"2015-08-20T20:50:46Z","updated_at":"2015-08-20T20:50:46Z","body":"I'm sorry for the confusion on my part. If I change the definition of `Column`, then yes, but the problem of `Unpackspec` still stands. You can't use a `RowParser` with `Column` because of the way that `Unpackspec` works for `Column`."}}