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.
24------------------------------------------------------------------------------
26{-# LANGUAGE OverloadedStrings, QuasiQuotes, ViewPatterns, RecordWildCards #-}
28module GenBuiltinTypes where
30import Prelude hiding ((++), concat)
33import Control.Arrow((&&&))
34import Control.Applicative
35import Control.Exception(bracket)
36import Database.PostgreSQL.Simple
37import Database.PostgreSQL.Simple.FromRow
38import Database.PostgreSQL.Simple.Ok
39import Database.PostgreSQL.Simple.Types(Oid(..))
40import Database.PostgreSQL.Simple.SqlQQ
41import qualified Data.ByteString.Char8 as B
42import Data.ByteString(ByteString)
43import qualified Data.ByteString.Lazy as L
44import qualified Blaze.ByteString.Builder as Blaze
45import qualified Blaze.ByteString.Builder.ByteString as Blaze
46import qualified Blaze.ByteString.Builder.Char8 as Blaze
48import Data.List ( sort, intersperse )
49import qualified Data.Map as Map
53(++) :: Monoid a => a -> a -> a
57concat :: Monoid a => [a] -> a
60data TypeInfo = TypeInfo
64 , typname :: ByteString
66 , rngsubtype :: Maybe Oid
69instance FromRow TypeInfo where
70 fromRow = TypeInfo <$> field <*> field <*> field <*> field <*> field <*> field
72type NameMap = Map.Map B.ByteString TypeInfo
74type OidMap = Map.Map Oid TypeInfo
76type TypeName = (B.ByteString, B.ByteString)
78type TypeNames = [TypeName]
80-- Note that the following syntax is "pgName <whitespace> hsName", though
81-- they default to the same thing if there is only one identifier
83typeNames = [typenames|
148_int2vector array_int2vector
150_regproc array_regproc
155_oidvector array_oidvector
157_varchar array_varchar
165_polygon array_polygon
167_macaddr array_macaddr
169_timestamp array_timestamp
172_timestamptz array_timestamptz
173_interval array_interval
174_numeric array_numeric
178_refcursor array_refcursor
179_regprocedure array_regprocedure
180_regoper array_regoper
181_regoperator array_regoperator
182_regclass array_regclass
183_regtype array_regtype
200instance IsString Blaze.Builder where
201 fromString = Blaze.fromByteString . fromString
203connectionString = "dbname=postgres"
205withPostgreSQL = bracket (connectPostgreSQL connectionString) close
207getTypeInfos :: TypeNames -> IO (OidMap, NameMap)
208getTypeInfos typnames = withPostgreSQL $ \conn -> do
209 infos <- query conn [sql|
211 (SELECT oid, typcategory, typdelim, typname, typelem
212 FROM pg_type WHERE typname IN ?)
213 SELECT types.*, rngsubtype FROM types LEFT JOIN pg_range ON oid = rngtypid
214 |] (Only (In (sort (map pg typnames))))
215 loop conn (oidMap infos) (nameMap infos) infos
217 oidMap = Map.fromList . map (typoid &&& id)
218 nameMap = Map.fromList . map (typname &&& id)
219 loop conn oids names infos = do
220 let unknowns = [ x | x <- map typelem infos ++
221 [ x | Just x <- map rngsubtype infos ],
223 not (Map.member x oids) ]
225 [] -> return (oids, names)
227 infos' <- query conn [sql|
229 (SELECT oid, typcategory, typdelim, typname, typelem
230 FROM pg_type WHERE oid IN ?)
231 SELECT types.*, rngsubtype
232 FROM types LEFT JOIN pg_range ON oid = rngtypid
233 |] (Only (In (sort unknowns)))
234 let oids' = oids `Map.union` oidMap infos'
235 names' = names `Map.union` nameMap infos'
236 loop conn oids' names' infos'
239 (oidmap, namemap) <- getTypeInfos typeNames
240 L.writeFile "../src/Database/PostgreSQL/Simple/TypeInfo/Static.hs"
241 (Blaze.toLazyByteString (renderFile oidmap namemap typeNames))
244showOid (Oid n) = show n
246renderOid :: NameMap -> TypeName -> Blaze.Builder
248 = case Map.lookup (pg name) byName of
249 Nothing -> error (B.unpack (pg name))
250 Just (showOid . typoid -> n) -> fromString n
251 ++ fromString (replicate (4 - length n) ' ')
253renderElem :: OidMap -> Oid -> Blaze.Builder
254renderElem byOid elemOid
255 | elemOid == Oid 0 = "Nothing"
256 | otherwise = case Map.lookup elemOid byOid of
257 Nothing -> error ("oid not found: " ++ show elemOid)
258 Just x -> "Just " ++ bs (typname x)
260renderTypeInfo :: OidMap -> TypeInfo -> TypeName -> Blaze.Builder
261renderTypeInfo byOid info name
262 | typcategory info == 'A' || typname info == "_record" =
263 let (Just typelem_info) = Map.lookup (typelem info) byOid
265 case lookup (typname typelem_info) typeNames of
266 Nothing -> error ( "type not found: "
267 ++ B.unpack( typname typelem_info)
268 ++ " (typelem of " ++ B.unpack (typname info)
273 , bs (hs name), " :: TypeInfo\n"
274 , bs (hs name), " = Array {\n"
275 , " typoid = ", fromString (show (typoid info)), ",\n"
276 , " typcategory = '", Blaze.fromChar (typcategory info), "',\n"
277 , " typdelim = '", Blaze.fromChar (typdelim info), "',\n"
278 , " typname = \"", bs (typname info), "\",\n"
279 , " typelem = ", bs typelem_hs_name, "\n"
282 | typcategory info == 'R' =
283 let (Just rngsubtype_oid) = rngsubtype info
284 (Just rngsubtype_info) = Map.lookup rngsubtype_oid byOid
286 case lookup (typname rngsubtype_info) typeNames of
287 Nothing -> error ( "type not found: "
288 ++ B.unpack (typname rngsubtype_info)
289 ++ " (rngsubtype of "
290 ++ B.unpack (typname info) ++ ")")
294 , bs (hs name), " :: TypeInfo\n"
295 , bs (hs name), " = Range {\n"
296 , " typoid = ", fromString (show (typoid info)), ",\n"
297 , " typcategory = '", Blaze.fromChar (typcategory info), "',\n"
298 , " typdelim = '", Blaze.fromChar (typdelim info), "',\n"
299 , " typname = \"", bs (typname info), "\",\n"
300 , " rngsubtype = ", bs rngsubtype_hs_name, "\n"
306 , bs (hs name), " :: TypeInfo\n"
307 , bs (hs name), " = Basic {\n"
308 , " typoid = ", fromString (show (typoid info)), ",\n"
309 , " typcategory = '", Blaze.fromChar (typcategory info), "',\n"
310 , " typdelim = '", Blaze.fromChar (typdelim info), "',\n"
311 , " typname = \"", bs (typname info), "\"\n"
315-- FIXME: add in any names that we need that we didn't specify, (i.e.
316-- the "unknowns" in getTypeInfos
317-- and munge them into a valid haskell identifier if needed.
318getNames :: NameMap -> TypeNames -> TypeNames
321bs = Blaze.fromByteString
327renderFile :: OidMap -> NameMap -> TypeNames -> Blaze.Builder
328renderFile byOid byName names = ([longstring|
329------------------------------------------------------------------------------
331-- Module: Database.PostgreSQL.Simple.TypeInfo
332-- Copyright: (c) 2011-2012 Leon P Smith
334-- Maintainer: Leon P Smith <leon@melding-monads.com>
335-- Stability: experimental
337-- This module contains portions of the @pg_type@ table that are relevant
338-- to postgresql-simple and are believed to not change between PostgreSQL
341------------------------------------------------------------------------------
343-- Note that this file is generated by tools/GenTypeInfo.hs, and should
344-- not be edited directly
346module Database.PostgreSQL.Simple.TypeInfo.Static
349|] ++ concat [ " , " ++ bs (hs name) ++ "\n"
350 | name <- names ] ++ [longstring|
353import Database.PostgreSQL.LibPQ (Oid(..))
354import Database.PostgreSQL.Simple.TypeInfo.Types
356staticTypeInfo :: Oid -> Maybe TypeInfo
357staticTypeInfo (Oid x) = case x of
358|] ++ concat [concat [ " ", renderOid byName name,
359 " -> Just ", bs (hs name), "\n"
365 ++ concat [ renderTypeInfo byOid typeInfo name
366 | name <- getNames byName names
367 , 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