This module contains the implementation of the Catalog data types and functions, and provides the api for the other type checking modules. NEW CATALOG! create new types from scratch 1st version only supports checking type names fix the read catalog code continue to ignore schemas start handling case sensitivity properly ignore modifiers = Catalog overview The main purpose of the catalog is to support typechecking. A secondary purpose is to be able to support documentation generation. == What information does the catalog contain? types: base types names of all types domains casts type categories (what is this?) tables, views, composite types operators: prefix ops postfix ops binary ops regular fns aggregate fns window fns triggers indexes sequences > {-# LANGUAGE DeriveDataTypeable,OverloadedStrings #-} > > module Database.HsSqlPpp.Internals.Catalog.CatalogInternal > (catLookupType > ,catLookupTableAndAttrs > ,catGetOpsMatchingName > -- temp stuff for old typeconversion > ,catLookupFns > ,catPreferredType > ,isOperatorName > ,catTypeCategory > ,catCast > ,catCompositePublicAttrs > ,catDomainBaseType > ,typeToCatName > ) where > > --import Control.Monad > --import Data.List > --import Data.Data > --import Data.Char > import Data.Maybe > import qualified Data.Map as M > import qualified Data.Set as S > import Database.HsSqlPpp.Internals.TypesInternal > --import Database.HsSqlPpp.Utils.Utils > import Data.Text (Text) > import qualified Data.Text as T > --import qualified Data.Text.Lazy as LT > import Database.HsSqlPpp.Internals.Catalog.CatalogTypes > --import Database.HsSqlPpp.Internals.Catalog.BaseCatalog > import Database.HsSqlPpp.Internals.Catalog.CatalogUtils ----------------------------------- types: What information does the catalog store and use on types? The basic set of types recorded here are: scalar types - the base types implemented outside of plpgsql domain types - this is a base type with a constraint (can composite types be used in a domain?) enum types - the usual. The values are strings and are case sensitive. I think you can overload the values so they can be members of more than one enum type named composite types - these are structs, created using 'create type x as', and also a composite type is implicitly created for each table and view definition with the same name as the table or view array types - not sure exactly how to handle this, generally, postgres automatically creates an array type for each scalar, domain, composite and enum type (any others?), and you can't use arrays of something if the type of the array isn't in the catalog. So lots of other types can't be made into arrays (what about arrays of arrays? don't know). Hssqlppp simply assumes that the array type of any type is available None of the types apart from these exist in the same way in postgres - so unnamed compositetypes, anonymous record type, unknown and pseudo type are used in less contexts. Maybe one way of looking at them is to consider them more like type generators which exist simply when they are used, and don't have to be declared up front. Only the above privileged types can be used for the types of columns in tables and views (I think?). for scalar types, no information is used apart from the fact that a type with the given name exists and can be referred to. for domain types, there is also the base type name, and the check constraint for enum types, name and the list of labels for named composite types, you need the name of the type, and the names and types of the fields for array types, you only need the base type name, array types don't have their own separate name todo: where should this utility live? Probably should be connected to the dialects > typeToCatName :: TypeExtra -> Either [TypeError] CatNameExtra > typeToCatName te = case teType te of > ScalarType t -> return > $ CatNameExtra t (tePrecision te) (teScale te) (teNullable te) > _ -> Left [InternalError "typeToCatName on a non scalar type"] ----------------------------------------------------------- queries gets a schema qualified catname, puts in the default 'public' if there is only one name component. This will be altered when schema search paths are implemented. > getCatName2 :: [NameComponent] -> (CatName,CatName) > -- todo: don't use error here > getCatName2 [] = error "empty name component in catalog code" > getCatName2 [a] = ("public",ncStrT a) > getCatName2 [a,b] = (ncStrT a, ncStrT b) > getCatName2 (_:xs) = getCatName2 xs TODO: add inverse of this operation, give a type, returns a typename > -- | takes a table name, and returns the exact table name (to deal > -- with quoting), and the public and private attr names > catLookupTableAndAttrs :: Catalog > -> [NameComponent] > -> Either [TypeError] ((Text,Text),[(Text,TypeExtra)], [(Text,Type)]) > catLookupTableAndAttrs cat nmcs = do > let n = getCatName2 nmcs > (pu,pv) <- maybe (Left [UnrecognisedRelation n]) Right > $ M.lookup n (catTables cat) > return (n,pu,pv) > catGetOpsMatchingName :: Catalog -> [NameComponent] -> [OperatorPrototype] > catGetOpsMatchingName cat nmcs = > let nm = getCatName nmcs > in concatMap (\f -> fromMaybe [] $ M.lookup nm $ f cat) > [catPrefixOps > ,catPostfixOps > ,catBinaryOps > ,catFunctions > ,catAggregateFunctions > ,catWindowFunctions] the TypeConversion module handles checking assignment compatibility, 'resolving result set types', and finding function call matches since this relies on some heavy algorithms to match postgress really complex overloading and implicit cast system. -------------------------------------------------------- old stuff chucked in to support the old typeconversion, to be promoted to new code or deleted as typeconversion is rewritten > catCompositePublicAttrs :: Catalog -> [CompositeFlavour] -> Text > -> Either [TypeError] [(Text,TypeExtra)] > catCompositePublicAttrs cat _flvs ty = do > (_,a,_) <- catLookupTableAndAttrs cat [Nmc $ T.unpack ty] > return a > catPreferredType :: Catalog -> Type -> Either [TypeError] Bool > catPreferredType cat ty = > fmap snd $ catGetCategoryInfo cat ty > > catCast :: Catalog -> CastContext -> Type -> Type -> Either [TypeError] Bool > catCast cat ctx from to = > case from of > t@(DomainType _) -> do > baseType <- catDomainBaseType cat t > cc <- catCast cat ctx baseType to > return $ (baseType == to) || > (cc || S.member (from, to, ctx) (catCasts cat)) > _ -> Right $ S.member (from, to, ctx) (catCasts cat) > > catDomainBaseType :: Catalog -> Type -> Either [TypeError] Type > catDomainBaseType cat (ScalarType ty) = > case M.lookup ty $ catDomainTypes cat of > Just n -> Right $ ScalarType n > Nothing -> Left [DomainDefNotFound $ ScalarType ty] > catDomainBaseType _cat ty = Left [DomainDefNotFound ty] > > catLookupFns :: Catalog -> Text -> [OperatorPrototype] > catLookupFns cat name = > catGetOpsMatchingName cat [Nmc $ T.unpack name] > catTypeCategory :: Catalog -> Type -> Either [TypeError] Text > catTypeCategory cat ty = > fmap fst $ catGetCategoryInfo cat ty > isOperatorName :: Text -> Bool > isOperatorName = T.any (`elem` ("+-*/<>=~!@#%^&|`?."::String)) > catGetCategoryInfo :: Catalog -> Type -> Either [TypeError] (Text, Bool) > catGetCategoryInfo cat ty = > case ty of > Pseudo (SetOfType _) -> Right ("", False) > AnonymousCompositeType _ -> Right ("", False) > ArrayType (Pseudo _) -> Right ("A",False) > Pseudo _ -> Right ("P",False) > _ -> case M.lookup ty $ catTypeCategories cat of > Nothing -> Left [InternalError $ "no type category for " ++ show ty] > Just x -> Right x