> {-# LANGUAGE OverloadedStrings,ScopedTypeVariables #-}
> module Database.HsSqlPpp.Dialects.SqlServer (sqlServerDialect) where

> import Database.HsSqlPpp.Internals.Dialect
> import Database.HsSqlPpp.Internals.Catalog.CatalogBuilder
> import Database.HsSqlPpp.Internals.Catalog.CatalogTypes
> --import Database.HsSqlPpp.Dialects.BaseCatalog
> import Database.HsSqlPpp.Dialects.Postgres
> --import Database.HsSqlPpp.Internals.Dialect
> --import Data.List
> import Data.Generics.Uniplate.Data

The sql server dialect is a crap modification to the postgresql
dialect at the moment.  After a bunch more tests are written, it
should be reimplemented separately from scratch

> sqlServerDialect :: Dialect
> sqlServerDialect = Dialect
>     {diName = "sqlServer"
>     ,diSyntaxFlavour = SqlServer
>     ,diCanonicalTypeNames =  [("timestamp", ["datetime"])
>                              -- todo: temp before sqlserver dialect is done properly
>                              -- this hack should probably move to the ansi dialect first
>                              ,("int1", ["tinyint"])
>                              ,("int2", ["smallint"])
>                              ,("int4", ["integer","int"])
>                              ,("int8", ["bigint"])
>                              ,("float4", ["real"])
>                              ,("float8", ["double precision","float","double"])
>                              -- probably some missing here
>                              ,("varchar", ["character varying"])
>                              ,("char", ["character"])
>                              ,("bool", ["boolean"])]
>     ,diTextTypes = ["char","varchar", "nvarchar"]
>     ,diDatetimeTypes = ["date","time","timestamp","interval"]
>     ,diNumberTypes = ["int2","int4","int8","numeric","float4","float8"]
>     ,namesForAnsiTypes = [("char","char")
>                          ,("varchar","varchar")
>                          ,("nvarchar","nvarchar")
>                          ,("bigint","int8")
>                          ,("boolean","bool")
>                          ,("numeric","numeric")
>                          ,("int","int4")
>                          ,("date","date")
>                          ,("time","time")
>                          ,("timestamp","timestamp")
>                          ] -- todo: these are postgres names
>     ,diDefaultCatalog = tsqlCatalog
>     }

> tsqlCatalog :: Catalog
> tsqlCatalog = either (error . show) id catr
>   where
>     catr = updateCatalog
>               (alterUpdates (deconstructCatalog
>                              (diDefaultCatalog postgresDialect)
>                              ++ additionalEntries))
>               emptyCatalog
>     -- change the counts to return int instead of long
>     alterUpdates = map $ \u -> case u of
>         CatCreateAggregate "count" ["any"] "int8" ->
>             CatCreateAggregate "count" ["any"] "int4"
>         CatCreateAggregate f [e] _ | f `elem` ["sum","avg"]
>                                    , e `elem` ["int1"
>                                               ,"int2"
>                                               ,"int4"] ->
>             CatCreateAggregate f [e] "int4"
>         CatCreateAggregate f ["float4"] _ | f `elem` ["sum","avg"] ->
>             CatCreateAggregate f ["float4"] "float8"
>         CatCreateAggregate f ["int8"] _ | f `elem` ["sum","avg"] ->
>             CatCreateAggregate f ["int8"] "int8"
>         _ -> u

>     additionalEntries =
>         int1fns ++
>         int12fns ++
>         [CatCreateScalarType "nvarchar"
>         ,CatCreateTypeCategoryEntry "nvarchar" ("S", False)
>         ,CatCreateBinaryOp "+" "varchar" "varchar" "varchar"
>         ,CatCreateFunction "getdate" [] False "timestamp"
>         ,CatCreateFunction "isnumeric" ["anyelement"] False "int4"
>         ,CatCreateFunction "grt_lengthconv" ["int4"] False "int4"
>         ,CatCreateFunction "isnull" ["anyelement","anyelement"] False "anyelement"
>         -- put these in to stop use the text only version and a bunch of casts
>         ,CatCreateFunction "replace" ["char", "char", "char"] False "char"
>         ,CatCreateFunction "replace" ["varchar", "varchar", "varchar"] False "varchar"
>         ,CatCreateFunction "replace" ["nvarchar", "nvarchar", "nvarchar"] False "nvarchar"
>         ,CatCreateFunction "patindex" ["char","char"] False "int4"
>         ,CatCreateFunction "patindex" ["varchar","varchar"] False "int4"
>         ,CatCreateFunction "patindex" ["nvarchar","nvarchar"] False "int4"
>         ,CatCreateFunction "isdate" ["varchar"] False "bool"
>         ,CatCreateFunction "isdate" ["char"] False "int4"
>         ,CatCreateFunction "isdate" ["nvarchar"] False "int4"
>         ,CatCreateFunction "len" ["nvarchar"] False "int4"
>         ,CatCreateFunction "len" ["varchar"] False "int4"
>         ,CatCreateAggregate "count_big" ["any"] "int8"
>         ,CatCreateFunction "datediff" ["int4","date","date"] False "int4"
>         ,CatCreateFunction "datediff" ["int4","timestamp","timestamp"] False "int4"
>         ,CatCreateFunction "dateadd" ["int4","int4","date"] False "date"
>         ,CatCreateFunction "dateadd" ["int4","int4","timestamp"] False "timestamp"
>         ,CatCreateFunction "datepart" ["int4","date"] False "int4"
>         ,CatCreateFunction "datepart" ["int4","timestamp"] False "int4"
>         ,CatCreateFunction "trunc" ["timestamp"] False "timestamp"
>         ,CatCreateCast "char" "varchar" ImplicitCastContext

>         ,CatCreateFunction "substring" ["nvarchar","int4","int4"] False "nvarchar"
>         ,CatCreateFunction "like" [ "nvarchar" , "nvarchar" ] False "bool"

postponed until we have better design
  example of a problem: in "float4 < int4", both arguments are cast to text,
  because it has higher priority (see CatCreateTypeCategoryEntry)

>         --,CatCreateCast "int1" "varchar" ImplicitCastContext
>         --,CatCreateCast "int2" "varchar" ImplicitCastContext
>         --,CatCreateCast "int4" "varchar" ImplicitCastContext
>         --,CatCreateCast "int8" "varchar" ImplicitCastContext
>         --,CatCreateCast "float4" "varchar" ImplicitCastContext
>         --,CatCreateCast "float8" "varchar" ImplicitCastContext
>         --,CatCreateCast "int1" "text" ImplicitCastContext
>         --,CatCreateCast "int2" "text" ImplicitCastContext
>         --,CatCreateCast "int4" "text" ImplicitCastContext
>         --,CatCreateCast "int8" "text" ImplicitCastContext
>         --,CatCreateCast "float4" "text" ImplicitCastContext
>         --,CatCreateCast "float8" "text" ImplicitCastContext
>         ]
>     -- find all the functions on int2 and replace int2 with int1
>     -- then find all the functions with int2 and int4, and
>     -- replace int2 with int1 and int4 with int2
>     -- really hacky
>     int1fns = let s = filter (\x -> replaceItp x && hasInt2 x)
>                              (deconstructCatalog (diDefaultCatalog postgresDialect))
>               in flip transformBi s $ \x -> case (x :: CatName) of
>                                        "int2" -> "int1"
>                                        _ -> x
>     int12fns = let s = filter (\x -> replaceItp x && hasInt2Int4 x)
>                               (deconstructCatalog (diDefaultCatalog postgresDialect))
>                in flip transformBi s $ \x -> case (x :: CatName) of
>                                        "int2" -> "int1"
>                                        "int4" -> "int2"
>                                        _ -> x
>     hasInt2 x = not $ null [() | ("int2" :: CatName) <- universeBi x]
>     hasInt2Int4 x = not $ null [() | ("int2" :: CatName) <- universeBi x
>                                    , ("int4" :: CatName) <- universeBi x]
>     replaceItp x = case x of
>                      CatCreateScalarType {} -> True
>                      CatCreateArrayType {} -> True
>                      CatCreatePrefixOp {} -> True
>                      CatCreateBinaryOp {} -> True
>                      CatCreateFunction f _ _ _ | f `elem` ["abs","float4","float8","int2","int4","mod","numeric"] -> True
>                      CatCreateAggregate f _ _ | f `elem` ["avg","max","min","sum"] -> True
>                      CatCreateCast a b _ | a == "int2" || b == "int2" -> True
>                      CatCreateTypeCategoryEntry {} -> True
>                      _ -> False

comparisons with all ints
abs
cast functions
avg to numeric max min sum
catcreatecast: float4,float8, int4,int8,numeric, from