1------------------------------------------------------------------------------
4-- Copyright: (c) 2011-2012 Leon P Smith
6-- Maintainer: Leon P Smith <leon@melding-monads.com>
7-- Stability: experimental
9-- Queries a PostgreSQL database for the Object IDs and other type
10-- information associated with typenames, and generates a module with
11-- data constants representing part of the pg_type table.
13-- Note that only some of the built-in types have stable type OIDs, and
14-- thus a TypeInfo module that contains user-defined types is liable to
15-- not work across different database instances. For these types, it is
16-- better for `FromField` to use the `typeinfo` operator that works for
17-- any type, whether or not it is in this module.
19-- `typeinfo` works because postgresql-simple will dynamically query
20-- the pg_types table the first time it receives a type OID it doesn't
21-- know about. It then constructs a TypeInfo record and stores it in
22-- a per-connection cache for later use.
25-- runghc -itools tools/GenTypeInfo.hs
27------------------------------------------------------------------------------
29{-# LANGUAGE OverloadedStrings, QuasiQuotes, ViewPatterns, RecordWildCards #-}
31module GenBuiltinTypes where
33import Prelude hiding ((++), concat)
36import Control.Arrow((&&&))
37import Control.Applicative
38import Control.Exception(bracket)
39import Database.PostgreSQL.Simple
40import Database.PostgreSQL.Simple.FromRow
41import Database.PostgreSQL.Simple.Ok
42import Database.PostgreSQL.Simple.Types(Oid(..))
43import Database.PostgreSQL.Simple.SqlQQ
44import qualified Data.ByteString.Char8 as B8
45import Data.ByteString(ByteString)
46import qualified Data.ByteString.Lazy as L
47import qualified Data.ByteString.Builder as Blaze
49import Data.List ( sort, intersperse )
50import qualified Data.Map as Map
54(++) :: Monoid a => a -> a -> a
58concat :: Monoid a => [a] -> a
61data TypeInfo = TypeInfo
65 , typname :: ByteString
67 , rngsubtype :: Maybe Oid
70instance FromRow TypeInfo where
71 fromRow = TypeInfo <$> field <*> field <*> field <*> field <*> field <*> field
73type NameMap = Map.Map B8.ByteString TypeInfo
75type OidMap = Map.Map Oid TypeInfo
77type TypeName = (B8.ByteString, B8.ByteString)
79type TypeNames = [TypeName]
81-- Note that the following syntax is "pgName <whitespace> hsName", though
82-- they default to the same thing if there is only one identifier
84typeNames = [typenames|
149_int2vector array_int2vector
151_regproc array_regproc
156_oidvector array_oidvector
158_varchar array_varchar
166_polygon array_polygon
168_macaddr array_macaddr
170_timestamp array_timestamp
173_timestamptz array_timestamptz
174_interval array_interval
175_numeric array_numeric
179_refcursor array_refcursor
180_regprocedure array_regprocedure
181_regoper array_regoper
182_regoperator array_regoperator
183_regclass array_regclass
184_regtype array_regtype
201connectionString = "dbname=postgres"
203withPostgreSQL = bracket (connectPostgreSQL connectionString) close
205getTypeInfos :: TypeNames -> IO (OidMap, NameMap)
206getTypeInfos typnames = withPostgreSQL $ \conn -> do
207 infos <- query conn [sql|
209 (SELECT oid, typcategory, typdelim, typname, typelem
210 FROM pg_type WHERE typname IN ?)
211 SELECT types.*, rngsubtype FROM types LEFT JOIN pg_range ON oid = rngtypid
212 |] (Only (In (sort (map pg typnames))))
213 loop conn (oidMap infos) (nameMap infos) infos
215 oidMap = Map.fromList . map (typoid &&& id)
216 nameMap = Map.fromList . map (typname &&& id)
217 loop conn oids names infos = do
218 let unknowns = [ x | x <- map typelem infos ++
219 [ x | Just x <- map rngsubtype infos ],
221 not (Map.member x oids) ]
223 [] -> return (oids, names)
225 infos' <- query conn [sql|
227 (SELECT oid, typcategory, typdelim, typname, typelem
228 FROM pg_type WHERE oid IN ?)
229 SELECT types.*, rngsubtype
230 FROM types LEFT JOIN pg_range ON oid = rngtypid
231 |] (Only (In (sort unknowns)))
232 let oids' = oids `Map.union` oidMap infos'
233 names' = names `Map.union` nameMap infos'
234 loop conn oids' names' infos'
237 (oidmap, namemap) <- getTypeInfos typeNames
238 L.writeFile "src/Database/PostgreSQL/Simple/TypeInfo/Static.hs"
239 (Blaze.toLazyByteString (renderFile oidmap namemap typeNames))
242showOid (Oid n) = show n
244renderOid :: NameMap -> TypeName -> Blaze.Builder
246 = case Map.lookup (pg name) byName of
247 Nothing -> error (B8.unpack (pg name))
248 Just (showOid . typoid -> n) -> fromString n
249 ++ fromString (replicate (4 - length n) ' ')
251renderElem :: OidMap -> Oid -> Blaze.Builder
252renderElem byOid elemOid
253 | elemOid == Oid 0 = "Nothing"
254 | otherwise = case Map.lookup elemOid byOid of
255 Nothing -> error ("oid not found: " ++ show elemOid)
256 Just x -> "Just " ++ bs (typname x)
258renderTypeInfo :: OidMap -> TypeInfo -> TypeName -> Blaze.Builder
259renderTypeInfo byOid info name
260 | typcategory info == 'A' || typname info == "_record" =
261 let (Just typelem_info) = Map.lookup (typelem info) byOid
263 case lookup (typname typelem_info) typeNames of
264 Nothing -> error ( "type not found: "
265 ++ B8.unpack( typname typelem_info)
266 ++ " (typelem of " ++ B8.unpack (typname info)
271 , bs (hs name), " :: TypeInfo\n"
272 , bs (hs name), " = Array {\n"
273 , " typoid = ", bs (hs name), "Oid,\n"
274 , " typcategory = '", bs $ B8.singleton (typcategory info), "',\n"
275 , " typdelim = '", bs $ B8.singleton (typdelim info), "',\n"
276 , " typname = \"", bs (typname info), "\",\n"
277 , " typelem = ", bs typelem_hs_name, "\n"
280 , bs (hs name), "Oid :: Oid\n"
281 , bs (hs name), "Oid = ", fromString (show (typoid info)), "\n"
282 , "{-# INLINE ", bs (hs name), "Oid #-}"
285 | typcategory info == 'R' =
286 let (Just rngsubtype_oid) = rngsubtype info
287 (Just rngsubtype_info) = Map.lookup rngsubtype_oid byOid
289 case lookup (typname rngsubtype_info) typeNames of
290 Nothing -> error ( "type not found: "
291 ++ B8.unpack (typname rngsubtype_info)
292 ++ " (rngsubtype of "
293 ++ B8.unpack (typname info) ++ ")")
297 , bs (hs name), " :: TypeInfo\n"
298 , bs (hs name), " = Range {\n"
299 , " typoid = ", bs (hs name), "Oid,\n"
300 , " typcategory = '", bs $ B8.singleton (typcategory info), "',\n"
301 , " typdelim = '", bs $ B8.singleton (typdelim info), "',\n"
302 , " typname = \"", bs (typname info), "\",\n"
303 , " rngsubtype = ", bs rngsubtype_hs_name, "\n"
306 , bs (hs name), "Oid :: Oid\n"
307 , bs (hs name), "Oid = ", fromString (show (typoid info)), "\n"
308 , "{-# INLINE ", bs (hs name), "Oid #-}"
314 , bs (hs name), " :: TypeInfo\n"
315 , bs (hs name), " = Basic {\n"
316 , " typoid = ", bs (hs name), "Oid,\n"
317 , " typcategory = '", bs $ B8.singleton (typcategory info), "',\n"
318 , " typdelim = '", bs $ B8.singleton (typdelim info), "',\n"
319 , " typname = \"", bs (typname info), "\"\n"
322 , bs (hs name), "Oid :: Oid\n"
323 , bs (hs name), "Oid = ", fromString (show (typoid info)), "\n"
324 , "{-# INLINE ", bs (hs name), "Oid #-}"
328-- FIXME: add in any names that we need that we didn't specify, (i.e.
329-- the "unknowns" in getTypeInfos
330-- and munge them into a valid haskell identifier if needed.
331getNames :: NameMap -> TypeNames -> TypeNames
340renderFile :: OidMap -> NameMap -> TypeNames -> Blaze.Builder
341renderFile byOid byName names = ([longstring|
342------------------------------------------------------------------------------
344-- Module: Database.PostgreSQL.Simple.TypeInfo
345-- Copyright: (c) 2011-2012 Leon P Smith
347-- Maintainer: Leon P Smith <leon@melding-monads.com>
348-- Stability: experimental
350-- This module contains portions of the @pg_type@ table that are relevant
351-- to postgresql-simple and are believed to not change between PostgreSQL
354------------------------------------------------------------------------------
356-- Note that this file is generated by tools/GenTypeInfo.hs, and should
357-- not be edited directly
359module Database.PostgreSQL.Simple.TypeInfo.Static
362|] ++ concat [ " , " ++ bs (hs name) ++ "\n"
363 ++ " , " ++ bs (hs name) ++ "Oid\n"
364 | name <- names ] ++ [longstring|
367import Database.PostgreSQL.LibPQ (Oid(..))
368import Database.PostgreSQL.Simple.TypeInfo.Types
370staticTypeInfo :: Oid -> Maybe TypeInfo
371staticTypeInfo (Oid x) = case x of
372|] ++ concat [concat [ " ", renderOid byName name,
373 " -> Just ", bs (hs name), "\n"
379 ++ concat [ renderTypeInfo byOid typeInfo name
380 | name <- getNames byName names
381 , let (Just typeInfo) = Map.lookup (pg name) byName])
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list