>
> {-# LANGUAGE PatternGuards,OverloadedStrings,TypeSynonymInstances,FlexibleInstances #-}
> module Database.HsSqlPpp.Internals.PrettyInternal
> (
> prettyStatements
>
> ,prettyQueryExpr
>
> ,prettyScalarExpr
> ,PrettyFlags(..)
> )
> where
>
> import Prelude hiding ((<>))
> import Text.PrettyPrint
> import Data.Maybe
>
> import Database.HsSqlPpp.Syntax hiding (ann)
> import Database.HsSqlPpp.Internals.AstInternal (
> Annotation(..)
> ,emptyAnnotation)
>
> import Database.HsSqlPpp.Internals.Dialect
todo: get rid of the types internal
the ImplicitCast ctor uses Type instead of syntax only, this should be
changed and then this import can be removed
> import Database.HsSqlPpp.Internals.TypesInternal
>
> import qualified Data.Text as T
> import qualified Data.Text.Lazy as L
> import Database.HsSqlPpp.Internals.Utils
Public functions
> data PrettyFlags =
> PrettyFlags
todo: actually use the dialect. this will forced when the parser is
adjusted to reject postgres only syntax when in sql server dialect
> {ppDialect :: Dialect
> }
> deriving (Show,Eq)
>
> prettyStatements :: PrettyFlags -> [Statement] -> L.Text
> prettyStatements f = prettyStatementsAnn f (const "")
>
>
>
>
this needs some work
> prettyStatementsAnn :: PrettyFlags -> (Annotation -> String) -> [Statement] -> L.Text
> prettyStatementsAnn flg f ast =
> renderText $ vcat (map (statement flg True f) ast) <> text "\n"
>
> prettyQueryExpr :: PrettyFlags -> QueryExpr -> L.Text
> prettyQueryExpr f ast = renderText (queryExpr f True True Nothing ast <> statementEnd True)
>
> prettyScalarExpr :: PrettyFlags -> ScalarExpr -> L.Text
> prettyScalarExpr f = renderText . scalExpr f
direct = true means attempt to pretty print straight to text
direct = false means pretty print to string then pack to text
no idea which is better, since pretty printing to text directly uses a
lot of Text.cons which might be pretty slow and bloated
> direct :: Bool
> direct = True
> renderText :: Doc -> L.Text
> renderText doc =
> if direct
> then fullRender (mode style) (lineLength style) (ribbonsPerLine style)
> dataTextPrettyer "" doc
> else L.pack $ render doc
> dataTextPrettyer :: TextDetails -> L.Text -> L.Text
> dataTextPrettyer (Chr c) s = L.cons c s
> dataTextPrettyer (Str s1) s2 = L.pack s1 `L.append` s2
> dataTextPrettyer (PStr s1) s2 = L.pack s1 `L.append` s2
Conversion routines - convert Sql asts into Docs
>
>
> statement :: PrettyFlags -> Bool -> (Annotation -> String) -> Statement -> Doc
> statement _flg _se _ca (AntiStatement s) = text $ "$(" ++ s ++ ")"
>
>
>
> statement flg se ca (QueryStatement ann s) =
> annot ca ann <+>
> queryExpr flg True True Nothing s <> statementEnd se
>
>
>
> statement flg se pa (Insert ann tb atts idata rt) =
> annot pa ann <+>
> text "insert into" <+> name tb
> <+> ifNotEmpty (parens . sepCsvMap nmc) atts
> $+$ queryExpr flg True True Nothing idata
> $+$ returning flg rt
> <> statementEnd se
>
> statement flg se ca (Update ann tb scs fr wh rt) =
> annot ca ann <+>
> text "update" <+> name tb <+> text "set"
> <+> sepCsvMap (set flg) scs
> <+> ifNotEmpty (\_ -> text "from" <+> sepCsvMap (tref flg) fr) fr
> <+> whr flg wh
> $+$ returning flg rt <> statementEnd se
>
> statement flg se ca (Delete ann tbl us wh rt) =
> annot ca ann <+>
> text "delete from" <+> name tbl
> <+> ifNotEmpty (\_ -> text "using" <+> sepCsvMap (tref flg) us) us
> <+> whr flg wh
> $+$ returning flg rt
> <> statementEnd se
>
> statement flg se ca (Truncate ann names ri casc) =
> annot ca ann <+>
> text "truncate"
> <+> sepCsvMap name names
> <+> case (diSyntaxFlavour $ ppDialect flg) of
> SqlServer -> empty
> _ -> text (case ri of
> RestartIdentity -> "restart identity"
> ContinueIdentity -> "continue identity")
> <+> cascade casc
> <> statementEnd se
>
>
>
> statement flg se ca (CreateTable ann tbl atts cns partition rep opts) =
> annot ca ann <+>
> text ("create " ++ (case rep of
> Replace -> "or replace "
> _ -> "") ++ "table")
> <+> name tbl <+> lparen
> $+$ nest 2 (vcat (csv (map (attrDef flg) atts ++ map (constraint flg) cns)))
> $+$ rparen
> $+$ case (diSyntaxFlavour $ ppDialect flg) of
> SqlServer -> empty
> _ -> (tablePartition partition)
> $+$ tableOpts flg opts
> <> statementEnd se
>
> statement flg se ca (AlterTable ann tnm op) =
> annot ca ann <+>
> text "alter table" <+> name tnm
> <+> alterOperation op <> statementEnd se
> where
> alterOperation (RenameTable _ nm) =
> text "rename to" <+> name nm
> alterOperation (RenameColumn _ old new) =
> text "rename column" <+> nmc old <+> "to" <+> nmc new
> alterOperation (AlterTableActions _ actions) = hcatCsvMap alterAction actions
> alterAction (AddColumn _ att) =
> text "add column" <+> attrDef flg att
> alterAction (DropColumn _ nm) =
> text "drop column" <+> nmc nm
> alterAction (AlterColumn _ nm action) =
> text "alter column" <+> nmc nm <+> alterColumnAction action
> alterAction (AddConstraint _ con) =
> text "add" <+> constraint flg con
> alterColumnAction (SetDataType _ t) =
> text "set data type" <+> typeName t
> alterColumnAction (SetNotNull _) =
> text "set not null"
> alterColumnAction (DropNotNull _) =
> text "drop not null"
> alterColumnAction (SetDefault _ def) =
> text "set default" <+> scalExpr flg def
> alterColumnAction (DropDefault _) =
> text "drop default"
>
> statement _flg se ca (CreateSequence ann nm incr minv maxv start cache) =
> annot ca ann <+>
> text "create sequence" <+> name nm <+>
> text "increment" <+> text (show incr) <+>
> maybe (text "no minvalue") ((text "minvalue" <+>) . (text . show)) minv <+>
> maybe (text "no maxvalue") ((text "maxvalue" <+>) . (text . show)) maxv <+>
> text "start with" <+> text (show start) <+>
> text "cache" <+> text (show cache) <> statementEnd se
>
> statement _flg se ca (AlterSequence ann nm op) =
> annot ca ann <+>
> text "alter sequence" <+> name nm
> <+> alterOperation op <> statementEnd se
> where
> alterOperation (AlterSequenceOwned _ o) =
> text "owned by" <+> name o
> alterOperation (AlterSequenceRename _ rnm) =
> text "rename to" <+> name rnm
> alterOperation (AlterSequenceActions _ actions) = hsep $ map alterAction actions
> alterAction (AlterSequenceIncrement _ incr) =
> text "increment by" <+> text (show incr)
> alterAction (AlterSequenceMin _ minv) =
> maybe (text "no minvalue") ((text "minvalue" <+>) . (text . show)) minv
> alterAction (AlterSequenceMax _ maxv) =
> maybe (text "no maxvalue") ((text "maxvalue" <+>) . (text . show)) maxv
> alterAction (AlterSequenceStart _ start) =
> text "start with" <+> text (show start)
> alterAction (AlterSequenceRestart _ restart) =
> text "restart" <+> text (maybe "" (("with " ++) . show) restart)
> alterAction (AlterSequenceCache _ cache) =
> text "cache" <+> text (show cache)
>
> statement flg se ca (CreateTableAs ann t rep sel) =
> annot ca ann <+>
> text ("create " ++ (case rep of
> Replace -> "or replace "
> _ -> "") ++ "table")
> <+> name t <+> text "as"
> $+$ queryExpr flg True True Nothing sel
> <> statementEnd se
>
> statement flg se ca (CreateFunction ann nm args retType rep lang body vol) =
> annot ca ann <+>
> text ("create " ++ (case rep of
> Replace -> "or replace "
> _ -> "") ++ "function")
> <+> name nm
> <+> parens (sepCsvMap paramDefn args)
> <+> text "returns" <+> typeName retType <+> text "as" <+> text "$$"
> $+$ functionBody body
> $+$ text "$$" <+> text "language"
> <+> text (case lang of
> Sql -> "sql"
> Plpgsql -> "plpgsql")
> <+> text (case vol of
> Volatile -> "volatile"
> Stable -> "stable"
> Immutable -> "immutable")
> <> statementEnd se
> where
> functionBody (SqlFnBody ann1 sts) =
> annot ca ann1 <+>
> nestedStatements flg ca sts
> functionBody (PlpgsqlFnBody ann1 blk) =
> annot ca ann1 <+>
> statement flg True ca blk
> paramDefn (ParamDef _ n t) = nmc n <+> typeName t
> paramDefn (ParamDefTp _ t) = typeName t
>
> statement flg se ca (Block ann lb decls sts) =
> annot ca ann <+>
> label lb <>
> ifNotEmpty (\l -> text "declare"
> $+$ nest 2 (vcat $ map varDefn l)) decls
> $+$ text "begin"
> $+$ nestedStatements flg ca sts
> $+$ text "end" <> statementEnd se
> where
> varDefn (VarDef _ n t v) =
> nmc n <+> typeName t
> <+> maybePrint (\x -> text ":=" <+> scalExpr flg x) v <> semi
> varDefn (VarAlias _ n n1) =
> nmc n <+> text "alias for" <+> name n1 <> semi
> varDefn (ParamAlias _ n p) =
> nmc n <+> text "alias for $" <> text (show p) <> semi
>
>
> statement flg se ca (CreateView ann nm cols sel) =
> annot ca ann <+>
> text "create view" <+> name nm
> <> case cols of
> Nothing -> empty
> Just cs -> parens (sepCsvMap nmc cs)
> <+> text "as"
> $+$ nest 2 (queryExpr flg True True Nothing sel) <> statementEnd se
>
> statement flg se ca (AlterView ann nm cols sel) =
> annot ca ann <+>
> text "alter view" <+> name nm
> <> case cols of
> Nothing -> empty
> Just cs -> parens (sepCsvMap nmc cs)
> <+> text "as"
> $+$ nest 2 (queryExpr flg True True Nothing sel) <> statementEnd se
>
> statement flg se ca (CreateDomain ann nm tp n ex) =
> annot ca ann <+>
> text "create domain" <+> name nm <+> text "as"
> <+> typeName tp <+> cname <+> checkExp ex <> statementEnd se
> where
> checkExp = maybePrint (\e -> text "check" <+> parens (scalExpr flg e))
> cname = if n == ""
> then empty
> else text "constraint" <+> ttext n
>
> statement _flg se ca (CreateDatabase ann nm) =
> annot ca ann <+>
> text "create database" <+> name nm <> statementEnd se
> statement _flg se ca (CreateSchema ann nm musr) =
> annot ca ann <+> text "create schema" <+> name nm
> <+> case musr of
> Nothing -> empty
> Just u -> text "authorization" <+> name u
> <> statementEnd se
> statement _flg se ca (AlterDatabase ann nm op) =
> annot ca ann <+> text "alter database" <+>
> name nm <+> alterDbOperation op <> statementEnd se
> where
> alterDbOperation (RenameDatabase _ nm') =
> text "rename to" <+> name nm'
> statement _flg se ca (AlterSchema ann snm op) =
> annot ca ann <+>
> text "alter schema" <+> name snm
> <+> alterOperation op <> statementEnd se
> where
> alterOperation (AlterSchemaName _ nsnm) =
> text "rename to" <+> name nsnm
> alterOperation (AlterSchemaOwner _ nunm) =
> text "owner to" <+> name nunm
> statement _flg se ca (DropFunction ann ifE fns casc) =
> annot ca ann <+>
> text "drop function"
> <+> ifExists ifE
> <+> sepCsvMap doFunction fns
> <+> cascade casc
> <> statementEnd se
> where
> doFunction (nm,types) =
> name nm <> parens (sepCsvMap typeName types)
>
> statement flg se ca (DropSomething ann dropType ifE names casc) =
> annot ca ann <+>
> text "drop"
> <+> text (case dropType of
> Table -> "table"
> View -> "view"
> Domain -> "domain"
> Type -> "type"
> Database -> "database"
> User -> "user"
> Login -> "login"
> Schema -> "schema")
> <+> ifExists ifE
> <+> sepCsvMap name names
> <+> case (diSyntaxFlavour $ ppDialect flg) of
> SqlServer -> empty
> _ -> cascade casc
> <> statementEnd se
>
> statement _flg se ca (DropTrigger ann ifE nam tbn casc) =
> annot ca ann <+>
> text "drop"
> <+> text "trigger"
> <+> ifExists ifE
> <+> nmc nam
> <+> text "on"
> <+> name tbn
> <+> cascade casc
> <> statementEnd se
>
> statement _flg se ca (CreateType ann nm atts) =
> annot ca ann <+>
> text "create type" <+> name nm <+> text "as" <+> lparen
> $+$ nest 2 (vcat (csv
> (map (\(TypeAttDef _ n t) -> nmc n <+> typeName t) atts)))
> $+$ rparen <> statementEnd se
>
> statement _flg se ca (CreateLanguage ann nm) =
> annot ca ann <+>
> text "create language" <+> ttext nm <> statementEnd se
>
> statement flg se ca (CreateTrigger ann nm wh events tbl firing fnName fnArgs) =
> annot ca ann <+>
> text "create trigger" <+> nmc nm
> <+> text (case wh of
> TriggerBefore -> "before"
> TriggerAfter -> "after")
> <+> evs
> <+> text "on" <+> name tbl
> <+> text "for" <+> text (case firing of
> EachRow -> "row"
> EachStatement -> "statement")
> <+> text "execute procedure" <+> name fnName
> <> parens (sepCsvMap (scalExpr flg) fnArgs) <> statementEnd se
> where
> evs = sep $ punctuate (text " or ") $ map
> (text . (\e -> case e of
> TInsert -> "insert"
> TUpdate -> "update"
> TDelete -> "delete"
> AntiTriggerEvent s -> "$(" ++ s ++ ")")) events
>
>
>
> statement _flg se ca (NullStatement ann) =
> annot ca ann <+> text "null" <> statementEnd se
> statement _flg se ca (ExitStatement ann lb) =
> annot ca ann <+> text "exit"
> <+> maybe empty ttext lb <> statementEnd se
>
> statement flg se ca (Into ann str is (QueryStatement _ q)) =
> annot ca ann <+>
> queryExpr flg True True (Just (str,is)) q <> statementEnd se
> statement flg se ca (Into ann str into st) =
> annot ca ann <+>
> statement flg False ca st
> <+> text "into"
> <> (if str
> then empty <+> text "strict"
> else empty)
> <+> sepCsvMap name into
> <> statementEnd se
>
> statement flg se ca (Assignment ann nm val) =
> annot ca ann <+>
> name nm <+> text ":=" <+> scalExpr flg val <> statementEnd se
>
> statement flg se ca (Return ann ex) =
> annot ca ann <+>
> text "return" <+> maybePrint (scalExpr flg) ex <> statementEnd se
>
> statement flg se ca (ReturnNext ann ex) =
> annot ca ann <+>
> text "return" <+> text "next" <+> scalExpr flg ex <> statementEnd se
>
> statement flg se ca (ReturnQuery ann sel) =
> annot ca ann <+>
> text "return" <+> text "query"
> <+> queryExpr flg True True Nothing sel <> statementEnd se
>
> statement flg se ca (Raise ann rt st exps) =
> annot ca ann <+>
> text "raise"
> <+> case rt of
> RNotice -> text "notice"
> RException -> text "exception"
> RError -> text "error"
> <+> scalExpr flg (StringLit emptyAnnotation st)
> <> ifNotEmpty (\e -> comma <+> csvExp flg e) exps
> <> statementEnd se
>
> statement flg se ca (ForQueryStatement ann lb i sel stmts) =
> annot ca ann <+>
> label lb <>
> text "for" <+> nmc i <+> text "in"
> <+> queryExpr flg True True Nothing sel <+> text "loop"
> $+$ nestedStatements flg ca stmts
> $+$ text "end loop" <> statementEnd se
>
> statement flg se ca (ForIntegerStatement ann lb var st en stmts) =
> annot ca ann <+>
> label lb <>
> text "for" <+> nmc var <+> text "in"
> <+> scalExpr flg st <+> text ".." <+> scalExpr flg en <+> text "loop"
> $+$ nestedStatements flg ca stmts
> $+$ text "end loop" <> statementEnd se
>
> statement flg se ca (WhileStatement ann lb ex stmts) =
> annot ca ann <+>
> label lb <>
> text "while" <+> scalExpr flg ex <+> text "loop"
> $+$ nestedStatements flg ca stmts
> $+$ text "end loop" <> statementEnd se
> statement flg se ca (LoopStatement ann lb stmts) =
> annot ca ann <+>
> label lb <>
> text "loop"
> $+$ nestedStatements flg ca stmts
> $+$ text "end loop" <> statementEnd se
>
> statement _flg se ca (ContinueStatement ann lb) =
> annot ca ann <+> text "continue"
> <+> maybe empty ttext lb <> statementEnd se
> statement flg se ca (Perform ann f@(App {})) =
> annot ca ann <+>
> text "perform" <+> scalExpr flg f <> statementEnd se
> statement _ _ _ (Perform _ x) =
>
> error $ "internal error: statement not supported for " ++ show x
>
> statement _flg se ca (CopyFrom ann tb cols src opts) =
> annot ca ann <+>
> text "copy" <+> name tb
> <+> ifNotEmpty (parens . sepCsvMap nmc) cols
> <+> text "from"
> <+> case src of
> CopyFilename s -> (quotes $ ttext s)
> <+> copyFromOpts opts
> <> statementEnd se
> Stdin -> text "stdin"
> <+> copyFromOpts opts
>
>
>
> <> statementEndNNL se
> statement flg se ca (CopyTo ann src fn opts) =
> annot ca ann <+>
> text "copy" <+> s src
> <+> text "to"
> <+> quotes (ttext fn)
> <+> copyToOpts opts
> <> statementEnd se
> where
> s (CopyTable tb cols) = name tb
> <+> ifNotEmpty (parens . sepCsvMap nmc) cols
> s (CopyQuery qry) = parens (queryExpr flg True True Nothing qry)
>
> statement _ _ ca (CopyData ann s) =
> annot ca ann <+>
> tltext s <> text "\\." <> newline
>
> statement flg se ca (If ann conds els) =
> if tsql
> then
> annot ca ann <+>
> text "if" <+> scalExpr flg (fst $ head conds)
> <+> blck (snd $ head conds)
> $+$ ifNotEmpty (\e -> text "else" <+> blck e) els
> <> statementEnd se
> else
> annot ca ann <+>
> text "if" <+> constraintd (head conds)
> $+$ vcat (map (\c -> text "elseif" <+> constraintd c) $ tail conds)
> $+$ ifNotEmpty (\e -> text "else" $+$ nestedStatements flg ca e) els
> $+$ text "end if" <> statementEnd se
> where
> constraintd (ex, sts) = scalExpr flg ex <+> text "then"
> $+$ nestedStatements flg ca sts
> tsql = diSyntaxFlavour (ppDialect flg) == SqlServer
> blck sts = sep [text "begin"
> ,nestedStatements flg ca sts
> ,text "end"]
> statement flg se ca (Execute ann s) =
> annot ca ann <+>
> text "execute" <+> scalExpr flg s <> statementEnd se
>
>
> statement flg se ca (CaseStatementSimple ann c conds els) =
> annot ca ann <+>
> text "case" <+> scalExpr flg c
> $+$ nest 2 (
> vcat (map (uncurry whenStatement) conds)
> $+$ elseStatement els
> ) $+$ text "end case" <> statementEnd se
> where
> whenStatement ex sts = text "when" <+> sepCsvMap (scalExpr flg) ex
> <+> text "then" $+$ nestedStatements flg ca sts
> elseStatement = ifNotEmpty (\s -> text "else"
> $+$ nestedStatements flg ca s)
> statement flg se ca (CaseStatement ann conds els) =
> annot ca ann <+>
> text "case"
> $+$ nest 2 (
> vcat (map (uncurry whenStatement) conds)
> $+$ elseStatement els
> ) $+$ text "end case" <> statementEnd se
> where
> whenStatement ex sts = text "when" <+> sepCsvMap (scalExpr flg) ex
> <+> text "then" $+$ nestedStatements flg ca sts
> elseStatement = ifNotEmpty (\s -> text "else"
> $+$ nestedStatements flg ca s)
> statement flg _se ca (DeclareStatement ann des) =
> annot ca ann <+>
> text "declare" <+> sepCsvMap de des
> where
> de (nm,ty,val) =
> ttext nm <+> typeName ty
> <+> maybe empty (\e -> text "=" <+> scalExpr flg e) val
>
>
>
> statement _flg se _ (Set _ n vs) =
> text "set" <+> ttext n <+> text "="
> <+> sepCsvMap (text . dv) vs <> statementEnd se
> where
> dv (SetStr _ s) = "'" ++ s ++ "'"
> dv (SetId _ i) = i
> dv (SetNum _ nm) = show nm
>
> statement _flg se _ (Notify _ n) =
> text "notify" <+> ttext n <> statementEnd se
> statement flg _se _ (ExecStatement _ nm args) =
> text "exec" <+> name nm <+> sepCsvMap (scalExpr flg) args
> statement _flg _se _ (CreateIndexTSQL _ nm obj cols) =
> text "create" <+> text "index"
> <+> nmc nm <+> text "on"
> <+> name obj <+> parens (sepCsvMap nmc cols)
> statement _flg _se _ (CreateUser _ nm pw) =
> text "create user" <+> name nm
> <+> text "with password=" <> quotes (text pw)
> statement _flg _se _ (CreateLogin _ nm pw) =
> text "create login" <+> name nm
> <+> text "with password=" <> quotes (text pw)
> statement _flg _se _ (AlterUser _ nm pw) =
> text "alter user" <+> name nm
> <+> text "with password=" <> quotes (text pw)
> statement _flg _se _ (AlterLogin _ nm pw) =
> text "alter login" <+> name nm
> <+> text "with password=" <> quotes (text pw)
> statementEnd :: Bool -> Doc
> statementEnd b = if b
> then semi <> newline
> else empty
> statementEndNNL :: Bool -> Doc
> statementEndNNL b = if b
> then semi
> else empty
Statement components
>
>
> queryExpr :: PrettyFlags -> Bool -> Bool -> Maybe (Bool,[Name]) -> QueryExpr -> Doc
> queryExpr flg writeSelect _ intoi (Select _ dis l tb wh grp hav
> order lim off hs) =
> (text (if writeSelect then "select" else "")
> <+> (case dis of
> All -> empty
> Distinct -> text "distinct"))
> <+> (case lim of
> Just lime | useTop -> text "top" <+> scalExpr flg lime
> _ -> empty)
> $+$ nest 2 (vcat $ catMaybes
> [fmap (\(str,is) -> text "into"
> <+> (if str
> then text "strict"
> else empty)
> <+> sepCsvMap name is) intoi
> ,Just $ nest 2 $ selectList flg l
> ,Just $ if null tb
> then empty
> else text "from" $+$ nest 2 (sepCsvMap (tref flg) tb)
> ,Just $ whr flg wh
> ,case grp of
> [] -> Nothing
> g -> Just $ text "group by" $+$ nest 2 (sepCsvMap (scalExpr flg) g)
> ,flip fmap hav $ \h -> text "having" $+$ nest 2 (scalExpr flg h)
> ,Just $ orderBy flg order
> ,if useTop then Nothing else flip fmap lim $ \lm -> text "limit" <+> scalExpr flg lm
> ,flip fmap off $ \offs -> text "offset" <+> scalExpr flg offs
> ,if null hs then Nothing else Just $ text "option" $+$ parens (sepCsvMap (text . prettyQueryHint) hs)
> ])
> where
> useTop = (diSyntaxFlavour $ ppDialect flg) == SqlServer
> prettyQueryHint QueryHintPartitionGroup = "partition group"
> prettyQueryHint QueryHintColumnarHostGroup = "columnar host group"
>
> queryExpr flg writeSelect topLev _ (CombineQueryExpr _ tp s1 s2) =
> let p = queryExpr flg writeSelect False Nothing s1
> $+$ (case tp of
> Except -> text "except"
> Union -> text "union"
> UnionAll -> text "union" <+> text "all"
> Intersect -> text "intersect")
> $+$ queryExpr flg True False Nothing s2
> in if topLev then p else parens p
> queryExpr flg _ _ _ (Values _ expss) =
> text "values" $$ nest 2 (vcat $ csv $ map (parens . csvExp flg) expss)
> queryExpr flg _ _ _ (WithQueryExpr _ wqs ex) =
> text "with" $$ nest 2 (vcat $ csv $ map pwq wqs)
> $+$ queryExpr flg True False Nothing ex
> where
> pwq (WithQuery _ nm cs ex1) =
> nmc nm <> case cs of
> Nothing -> empty
> Just cs' -> parens $ sepCsvMap nmc cs'
> <+> text "as"
> <+> parens (queryExpr flg True False Nothing ex1)
> name :: Name -> Doc
> name (Name _ ns) = nmcs ns
> name (AntiName n) = text ("$n(" ++ n ++ ")")
> nmcs :: [NameComponent] -> Doc
> nmcs ns = hcat $ punctuate (text ".") $ map nmc ns
> nmc :: NameComponent -> Doc
> nmc (Nmc ns) = ttext ns
> nmc (QNmc ns) = doubleQuotes $ ttext ns
> nmc (AntiNameComponent n) = text ("$m(" ++ n ++ ")")
>
> tref :: PrettyFlags -> TableRef -> Doc
> tref _ (Tref _ f) = name f
> tref flg (SubTref _ sub) =
> parens (queryExpr flg True True Nothing sub)
> tref flg (FunTref _ f@(App {})) = scalExpr flg f
> tref _flg (FunTref _ x) =
>
> error $ "internal error: node not supported in function tref: "
> ++ show x
> tref flg (TableRefParens _ t) = parens (tref flg t)
> tref flg (OdbcTableRef _ t) = text "{oj" <+> tref flg t <> text "}"
> tref flg (TableAlias _ t tr) = maybeParen flg tr <+> text "as" <+> nmc t
>
>
> tref flg (FullAlias _ t s tr) =
> maybeParen flg tr <+> text "as"
> <+> nmc t <> parens (sepCsvMap nmc s)
> tref flg (JoinTref _ t1 nat jt ht t2 ex) =
> sep [tref flg t1
> ,hsep ([case nat of
> Natural -> text "natural"
> Unnatural -> empty
> ,text $ case jt of
> Inner -> "inner"
> Cross -> "cross"
> LeftOuter -> "left outer"
> RightOuter -> "right outer"
> FullOuter -> "full outer"]
> ++ maybe [] (\h -> [text $ case h of
> Merge -> "merge"
> Loop -> "loop"
> Hash -> "hash"])
> ht
> ++ [text "join"])
> ,tref flg t2
> ,maybePrint (nest 2 . joinScalarExpr) ex]
> where
> joinScalarExpr (JoinOn _ e) = text "on" <+> scalExpr flg e
> joinScalarExpr (JoinUsing _ ids) =
> text "using" <+> parens (sepCsvMap nmc ids)
todo: don't want to do this here since it changes pretty . parse = id
so if you don't add the parens explicitly you get incorrect/ invalid
syntax maybe should error instead of silently breaking
> maybeParen :: PrettyFlags -> TableRef -> Doc
>
> maybeParen = tref
> direction :: Direction -> Doc
> direction d = text $ case d of
> Asc -> "asc"
> Desc -> "desc"
>
> nullsOrder :: NullsOrder -> Doc
> nullsOrder d = case d of
> NullsDefault -> empty
> NullsFirst -> text "nulls " <+> text "first"
> NullsLast -> text "nulls " <+> text "last"
>
> whr :: PrettyFlags -> Maybe ScalarExpr -> Doc
> whr flg (Just ex) = text "where" $+$ nest 2 (scalExpr flg ex)
> whr _ Nothing = empty
>
> selectList :: PrettyFlags -> SelectList -> Doc
> selectList flg (SelectList _ ex) =
> sepCsvMap selectItem ex
> where
> selectItem (SelectItem _ ex1 nm) = scalExprSl flg ex1 <+> text "as" <+> nmc nm
> selectItem (SelExp _ e) = scalExprSl flg e
>
> cascade :: Cascade -> Doc
> cascade casc = text $ case casc of
> Cascade -> "cascade"
> Restrict -> "restrict"
> copyToOpts :: [CopyToOption] -> Doc
> copyToOpts opts =
> ifNotEmpty (const $ "with" <+> sep (map po opts)) opts
> where
> po (CopyToFormat s) = text "format" <+> text s
> po (CopyToDelimiter s) = text "delimiter" <+> quotes (text s)
> po (CopyToErrorLog s) = text "error_log" <+> quotes (text s)
> po (CopyToErrorVerbosity s) = text "error_verbosity" <+> int s
> copyFromOpts :: [CopyFromOption] -> Doc
> copyFromOpts opts =
> ifNotEmpty (const $ "with" <+> sep (map po opts)) opts
> where
> po (CopyFromFormat s) = text "format" <+> text s
> po (CopyFromDelimiter s) = text "delimiter" <+> quotes (text s)
> po (CopyFromErrorLog s) = text "error_log" <+> quotes (text s)
> po (CopyFromErrorVerbosity s) = text "error_verbosity" <+> int s
> po (CopyFromParsers s) = text "parsers" <+> quotes (text s)
> po (CopyFromDirectory) = text "directory"
> po (CopyFromOffset i) = text "offset" <+> integer i
> po (CopyFromLimit i) = text "limit" <+> integer i
> po (CopyFromErrorThreshold i) = text "stop after" <+> int i <+> text "errors"
> po (CopyFromNewlineFormat n) = text "record delimiter" <+> text n
>
>
> constraint :: PrettyFlags -> Constraint -> Doc
> constraint _flg (UniqueConstraint _ n c) =
> mname n <+> text "unique"
> <+> parens (sepCsvMap nmc c)
> constraint _flg (PrimaryKeyConstraint _ n p) =
> mname n <+>
> text "primary key"
> <+> parens (sepCsvMap nmc p)
> constraint flg (CheckConstraint _ n c) =
> mname n <+> text "check" <+> parens (scalExpr flg c)
> constraint _flg (ReferenceConstraint _ n at tb rat ondel onupd) =
> mname n <+>
> text "foreign key" <+> parens (sepCsvMap nmc at)
> <+> text "references" <+> name tb
> <+> ifNotEmpty (parens . sepCsvMap nmc) rat
> <+> text "on update" <+> cascade onupd
> <+> text "on delete" <+> cascade ondel
>
> mname :: String -> Doc
> mname n = if n == ""
> then empty
> else text "constraint" <+> text n
>
> returning :: PrettyFlags -> Maybe SelectList -> Doc
> returning flg l = case l of
> Nothing -> empty
> Just ls -> nest 2 (text "returning" <+> selectList flg ls)
>
> ifExists :: IfExists -> Doc
> ifExists i = case i of
> Require -> empty
> IfExists -> text "if exists"
>
> tablePartition :: Maybe TablePartitionDef -> Doc
> tablePartition Nothing = text ""
> tablePartition (Just (TablePartitionDef _ cn tf interval)) =
> text "partition by range" <+> parens (nmc cn)
> <> parens ((text "every") <+> (text $ show tf) <+> (intervalify interval))
> where
> intervalify = \x-> text $ case x of
> Year -> "years"
> Month -> "months"
> Day -> "days"
> Hour -> "hours"
> Minute -> "minutes"
> Second -> "seconds"
> Millisecond -> "milliseconds"
>
> attrDef :: PrettyFlags -> AttributeDef -> Doc
> attrDef flg (AttributeDef _ n t cons opts) =
> nmc n <+> typeName t
> <+> hsep (map cCons cons)
> <+> tableOpts flg opts
> where
> cCons (DefaultConstraint _ cn e) =
> mname cn <+> text "default" <+> scalExpr flg e
> cCons (NullConstraint _ cn) =
> mname cn <+> text "null"
> cCons (NotNullConstraint _ cn) =
> mname cn <+> text "not null"
> cCons (RowCheckConstraint _ cn ew) =
> mname cn <+> text "check" <+> parens (scalExpr flg ew)
> cCons (RowUniqueConstraint _ cn) =
> mname cn <+> text "unique"
> cCons (RowPrimaryKeyConstraint _ cn) =
> mname cn <+> text "primary key"
> cCons (RowReferenceConstraint _ cn tb att ondel onupd) =
> mname cn <+> text "references" <+> name tb
> <+> maybePrint (parens . nmc) att
> <+> text "on delete" <+> cascade ondel
> <+> text "on update" <+> cascade onupd
> cCons (IdentityConstraint _ cn si) =
> mname cn <+> text "identity" <> text (maybe "" show si)
> tableOpts :: PrettyFlags -> [TableOption] -> Doc
> tableOpts _ [] = empty
> tableOpts flg as = text "with" <+> text "options"
> <+> parens (nest 4 $ sep $ map to as)
> where
> to (TableOptionKeywords ks) = hsep (map text ks)
> to (TableOptionStringVal nm v) = tov nm [scalExpr flg (StringLit emptyAnnotation v)]
> to (TableOptionNameVal nm v) = tov nm $ map name v
> to (TableOptionNumberVal nm v) = tov nm [text v]
> tov nm x = hsep (map text nm ++ [text "="] ++ x)
>
>
> nestedStatements :: PrettyFlags -> (Annotation -> String) -> [Statement] -> Doc
> nestedStatements flg pa = nest 2 . vcat . map (statement flg True pa)
>
> typeName :: TypeName -> Doc
> typeName (SimpleTypeName _ s) = name s
> typeName (PrecTypeName _ s i) = name s <> parens(integer i)
> typeName (Prec2TypeName _ s i i1) = name s <> parens (sepCsv [integer i, integer i1])
> typeName (ArrayTypeName _ t) = typeName t <> text "[]"
> typeName (SetOfTypeName _ t) = text "setof" <+> typeName t
>
> ppType:: Type -> Doc
> ppType (ScalarType t) = text "scalar type" <> parens (text $ T.unpack t)
> ppType (DomainType t) = text "domain type" <> parens (text $ T.unpack t)
> ppType (EnumType t) = text "enum type" <> parens (text $ T.unpack t)
> ppType (UnknownType) = text "unknown type"
> ppType (ArrayType t) = text "array type" <> parens (ppType t)
> ppType (NamedCompositeType t) = text "named composite type" <> parens (text $ T.unpack t)
> ppType (CompositeType ts)
> = text "composite type"
> <> brackets (sepCsv
> $ map (\(t,te)
> -> parens $ sepCsv [text (T.unpack t), typeExtra te])
> ts)
> ppType (TrefType ts)
> = text "tref type"
> <> brackets (sepCsv
> $ map (\((t1,t2),te)
> -> parens $ sepCsv
> [parens $ sepCsv
> [text (T.unpack t1)
> ,text (T.unpack t2)]
> , typeExtra te])
> ts)
> ppType (AnonymousCompositeType ts)
> = text "anonymous composite type"
> <> brackets (sepCsv $ map ppType ts)
> ppType (Pseudo _) = text "pseudo type"
>
> typeExtra:: TypeExtra -> Doc
> typeExtra te = parens $ sepCsv
> [ppType (teType te)
> ,ppPrec "precision" (tePrecision te)
> ,ppPrec "scale" (teScale te)
> ,ppNullability (teNullable te)]
> ppPrec :: String -> Maybe Int -> Doc
> ppPrec precType prec = case prec of
> Nothing -> text $ "no " ++ precType
> Just p -> text $ precType ++ ' ':show p
> ppNullability :: Bool -> Doc
> ppNullability n = text $ (if n then "" else "not ") ++ "nullable"
>
>
>
> scalExpr :: PrettyFlags -> ScalarExpr -> Doc
> scalExpr flg (Parens _ e) = parens (scalExpr flg e)
> scalExpr _ (AntiScalarExpr s) = text $ "$(" ++ s ++ ")"
> scalExpr _ (Star _) = text "*"
> scalExpr _ (QStar _ i) = nmc i <> text ".*"
> scalExpr _ (Identifier _ (AntiName _)) =
>
> error "Antiname component"
> scalExpr _flg (Identifier _a (Name _ is)) =
> hcat (punctuate (text ".") (map nmc is))
> scalExpr _ (NumberLit _ n) = ttext n
> scalExpr _ (StringLit _ s) =
> text "'" <> ttext replaceQuotes <> text "'"
> where
> replaceQuotes = replace "'" "''" s
>
> scalExpr flg (SpecialOp _ n es) =
> case getTName n of
> Just "arrayctor" -> text "array" <> brackets (csvExp flg es)
> Just "between" -> scalExpr flg (head es) <+> text "between"
> <+> scalExpr flg (es !! 1)
> <+> text "and"
> <+> scalExpr flg (es !! 2)
> Just "notbetween" -> scalExpr flg (head es) <+> text "not between"
> <+> scalExpr flg (es !! 1)
> <+> text "and"
> <+> scalExpr flg (es !! 2)
> Just "substring" -> text "substring"
> <> parens (scalExpr flg (head es)
> <+> text "from" <+> scalExpr flg (es !! 1)
> <+> text "for" <+> scalExpr flg (es !! 2))
> Just "arraysub" ->
> case es of
> (Identifier _ i : es1) -> name i
> <> brackets (csvExp flg es1)
> (e:es') -> scalExpr flg e
> <> brackets (csvExp flg es')
> _ -> error $ "bad args to !arraysub: " ++ show es
> Just "rowctor" -> text "row" <> parens (sepCsvMap (scalExpr flg) es)
> x -> error $ "bad special operator name: " ++ show x
> scalExpr flg (BinaryOp _ n e0 e1) =
> case getTName n of
> Just "and" | otherwise -> sep [scalExpr flg e0
> ,text "and"
> ,scalExpr flg e1]
> Just n' | Just n'' <- lookup n' [("or","or")
> ,("like","like")
> ,("rlike","rlike")
> ,("notlike","not like")] ->
> scalExpr flg e0
> <+> text n''
> <+> scalExpr flg e1
> Just "."
>
> -> scalExpr flg e0 <> text "." <> scalExpr flg e1
> Just n' -> scalExpr flg e0 <+> ttext n' <+> scalExpr flg e1
> Nothing -> error $ "bad binary operator name:" ++ show n
> scalExpr flg (PrefixOp _ n e0)
> | Just "not" <- getTName n =
> text "not" <+> scalExpr flg e0
> | Just n' <- getTName n =
> ttext n'
> <+> scalExpr flg e0
> | otherwise = error $ "bad prefix operator name:" ++ show n
> scalExpr flg (PostfixOp _ n e0)
> | Just n' <- getTName n >>= flip lookup [("isnull", "is null")
> ,("isnotnull", "is not null")] =
> scalExpr flg e0 <+> ttext n'
> | Just n' <- getTName n =
> scalExpr flg e0 <+> ttext n'
> | otherwise = error $ "bad postfix operator name:" ++ show n
> scalExpr flg (App _ n es) =
> name n <> parens (csvExp flg es)
>
> scalExpr _ (BooleanLit _ b) = bool b
> scalExpr flg (InPredicate _ att t lst) =
> scalExpr flg att <+> (if not t then text "not" else empty) <+> text "in"
> <+> parens (case lst of
> InList _ expr -> csvExp flg expr
> InQueryExpr _ sel -> queryExpr flg True True Nothing sel)
> scalExpr flg (LiftApp _ op flav args) =
> scalExpr flg (head args) <+> name op
> <+> text (case flav of
> LiftAny -> "any"
> LiftAll -> "all")
> <+> parens (scalExpr flg $ head $ tail args)
> scalExpr flg (ScalarSubQuery _ s) = parens (queryExpr flg True True Nothing s)
> scalExpr _ (NullLit _) = text "null"
> scalExpr flg (WindowApp _ fn part order frm) =
> scalExpr flg fn <+> text "over"
> <+> parens (if hp || ho
> then (if hp
> then text "partition by" <+> csvExp flg part
> else empty)
> <+> orderBy flg order
> <+> frameStuff
> else empty)
> where
> hp = not (null part)
> ho = not (null order)
> frameStuff = case frm of
> Nothing -> empty
> Just FrameUnboundedPreceding -> text "range unbounded preceding"
> Just FrameUnboundedFull -> text "range between unbounded preceding and unbounded following"
> Just FrameRowsUnboundedPreceding -> text "rows unbounded preceding"
>
> scalExpr flg (AggregateApp _ d (App _ fn es) o) =
> name fn <> parens ((case d of
> All -> text "all"
> Distinct -> text "distinct")
> <+> csvExp flg es
> <+> orderBy flg o)
> scalExpr _ a@(AggregateApp {}) = error $ "bad syntax for aggregate function" ++ show a
> scalExpr flg (Case _ whens els) =
> text "case"
> $+$ nest 2 (vcat (map whn whens)
> $+$ maybePrint (\e -> text "else" <+> scalExpr flg e) els)
> $+$ text "end"
> where
> whn (ex1, ex2) =
> text "when" <+> sepCsvMap (scalExpr flg) ex1
> <+> text "then" <+> scalExpr flg ex2
>
> scalExpr flg (CaseSimple _ val whens els) =
> text "case" <+> scalExpr flg val
> $+$ nest 2 (vcat (map whn whens)
> $+$ maybePrint (\e -> text "else" <+> scalExpr flg e) els)
> $+$ text "end"
> where
> whn (ex1, ex2) =
> text "when" <+> sepCsvMap (scalExpr flg) ex1
> <+> text "then" <+> scalExpr flg ex2
>
> scalExpr _ (PositionalArg _ a) = text "$" <> integer a
> scalExpr _ (Placeholder _) = text "?"
> scalExpr flg (Exists _ s) =
> text "exists" <+> parens (queryExpr flg True True Nothing s)
> scalExpr flg (Cast _ ex t) = text "cast" <> parens (scalExpr flg ex
> <+> text "as"
> <+> typeName t)
> scalExpr flg (ImplicitCast _ ex te) = text "impl_cast" <> parens (scalExpr flg ex
> <+> text "as"
> <+> typeExtra te)
> scalExpr flg (TypedStringLit a t s) =
> typeName t <+> scalExpr flg (StringLit a s)
> scalExpr flg (Interval a v f p) =
> text "interval" <+> scalExpr flg (StringLit a v)
> <+> intervalField <+> precision
> where
> intervalField =
> text $ case f of
> IntervalYear -> "year"
> IntervalMonth -> "month"
> IntervalDay -> "day"
> IntervalHour -> "hour"
> IntervalMinute -> "minute"
> IntervalSecond -> "second"
> IntervalYearToMonth -> "year to month"
> IntervalDayToHour -> "day to hour"
> IntervalDayToMinute -> "day to minute"
> IntervalDayToSecond -> "day to second"
> IntervalHourToMinute -> "hour to minute"
> IntervalHourToSecond -> "hour to second"
> IntervalMinuteToSecond -> "minute to second"
> precision = case p of
> Nothing -> empty
> Just i -> parens (int i)
> scalExpr flg (Extract _ f e) =
> text "extract"
> <> parens (text field <+> text "from" <+> scalExpr flg e)
> where
> field =
> case f of
> ExtractCentury -> "century"
> ExtractDay -> "day"
> ExtractDecade -> "decade"
> ExtractDow -> "dow"
> ExtractDoy -> "doy"
> ExtractEpoch -> "epoch"
> ExtractHour -> "hour"
> ExtractIsodow -> "isodow"
> ExtractIsoyear -> "isoyear"
> ExtractMicroseconds -> "microseconds"
> ExtractMillennium -> "millennium"
> ExtractMilliseconds -> "milliseconds"
> ExtractMinute -> "minute"
> ExtractMonth -> "month"
> ExtractQuarter -> "quarter"
> ExtractSecond -> "second"
> ExtractTimezone -> "timezone"
> ExtractTimezoneHour -> "timezone_hour"
> ExtractTimezoneMinute -> "timezone_minute"
> ExtractWeek -> "week"
> ExtractYear -> "year"
> scalExpr _flg (OdbcLiteral _ t s) =
> text "{" <> lt t <+> quotes (text s) <> text "}"
> where
> lt OLDate = "d"
> lt OLTime = "t"
> lt OLTimestamp = "ts"
> scalExpr flg (OdbcFunc _ e) =
> text "{fn" <+> scalExpr flg e <> text "}"
> scalExprSl :: PrettyFlags -> ScalarExpr -> Doc
> scalExprSl flg (App _ f es) | Just "." <- getTName f
> , [a@(Identifier _ _), b] <- es =
> scalExprSl flg a <> text "." <> scalExprSl flg b
> scalExprSl flg x = scalExpr flg x
>
> set :: PrettyFlags -> SetClause -> Doc
> set flg (SetClause _ a e) =
> nmc a <+> text "=" <+> scalExpr flg e
> set flg (MultiSetClause _ is (SpecialOp _ f es)) | Just "rowctor" <- getTName f =
> parens (sepCsvMap nmc is) <+> text "="
> <+> parens (sepCsvMap (scalExpr flg) es)
> set _ a = error $ "bad expression in set in update: " ++ show a
>
>
>
>
>
> csvExp :: PrettyFlags -> [ScalarExpr] -> Doc
> csvExp flg = hcatCsvMap (scalExpr flg)
>
> maybePrint :: (t -> Doc) -> Maybe t -> Doc
> maybePrint f c =
> case c of
> Nothing -> empty
> Just a -> f a
>
> csv :: [Doc] -> [Doc]
> csv = punctuate comma
>
> hcatCsv :: [Doc] -> Doc
> hcatCsv = hcat . csv
> sepCsv :: [Doc] -> Doc
> sepCsv = sep . csv
>
> ifNotEmpty :: ([a] -> Doc) -> [a] -> Doc
> ifNotEmpty c l = if null l then empty else c l
>
> hcatCsvMap :: (a -> Doc) -> [a] -> Doc
> hcatCsvMap ex = hcatCsv . map ex
> sepCsvMap :: (a -> Doc) -> [a] -> Doc
> sepCsvMap ex = sepCsv . map ex
> orderBy :: PrettyFlags -> [(ScalarExpr,Direction,NullsOrder)] -> Doc
> orderBy _ [] = empty
> orderBy flg os =
> text "order by"
> $+$ nest 2 (sepCsvMap (\(oe,od,on) -> scalExpr flg oe
> <+> direction od <+> nullsOrder on) os)
>
>
>
> bool :: Bool -> Doc
> bool b = if b then text "true" else text "false"
>
> newline :: Doc
> newline = text "\n"
>
> annot :: (Annotation -> String) -> Annotation -> Doc
> annot ca a = let s = ca a
> in if s == ""
> then empty
> else text "/*\n" <+> text s
> <+> text "*/\n"
> label :: Maybe String -> Doc
> label =
> maybe empty (\l -> text "<<"
> <+> text l
> <+> text ">>" <> text "\n")
util: to be removed when outputting names is fixed
> getTName :: Name -> Maybe String
> getTName (Name _ [n]) = Just $ ncStr n
> getTName _ = Nothing
> ttext :: String -> Doc
> ttext = text
> tltext :: String -> Doc
> tltext = text