>
>
> module Database.HsSqlPpp.Pretty (
>
> printStatements
> ,printStatementsAnn
> ,printQueryExpr
>
> ,printScalarExpr
> ,printQueryExprNice
> )
> where
>
> import Text.PrettyPrint
>
>
> import Data.Maybe
>
> import Database.HsSqlPpp.Ast
> import Database.HsSqlPpp.Annotation
> import Database.HsSqlPpp.Catalog
> import Database.HsSqlPpp.Utils.Utils
--------------------------------------------------------------------------------
Public functions
>
> printStatements :: StatementList -> String
> printStatements = printStatementsAnn (const "")
>
>
>
>
> printStatementsAnn :: (Annotation -> String) -> StatementList -> String
> printStatementsAnn f ast = render $ vcat (map (statement False True f) ast) <> text "\n"
>
> printQueryExpr :: QueryExpr -> String
> printQueryExpr ast = render (queryExpr False True True Nothing ast <> statementEnd True)
>
> printScalarExpr :: ScalarExpr -> String
> printScalarExpr = render . scalExpr False
>
>
> printQueryExprNice :: QueryExpr -> String
> printQueryExprNice ast = render (queryExpr True True True Nothing ast <> statementEnd True)
-------------------------------------------------------------------------------
Conversion routines - convert Sql asts into Docs
>
>
> statement :: Bool -> Bool -> (Annotation -> String) -> Statement -> Doc
> statement _nice _se _ca (AntiStatement s) = text $ "$(" ++ s ++ ")"
>
>
>
> statement nice se ca (QueryStatement ann s) =
> annot ca ann <+>
> queryExpr nice True True Nothing s <> statementEnd se
>
> --dml
>
> statement nice se pa (Insert ann tb atts idata rt) =
> annot pa ann <+>
> text "insert into" <+> name tb
> <+> ifNotEmpty (parens . sepCsvMap nmc) atts
> $+$ queryExpr nice True True Nothing idata
> $+$ returning nice rt
> <> statementEnd se
>
> statement nice se ca (Update ann tb scs fr wh rt) =
> annot ca ann <+>
> text "update" <+> name tb <+> text "set"
> <+> sepCsvMap (set nice) scs
> <+> ifNotEmpty (\_ -> text "from" <+> sepCsvMap (tref nice) fr) fr
> <+> whr nice wh
> $+$ returning nice rt <> statementEnd se
>
> statement nice se ca (Delete ann tbl us wh rt) =
> annot ca ann <+>
> text "delete from" <+> name tbl
> <+> ifNotEmpty (\_ -> text "using" <+> sepCsvMap (tref nice) us) us
> <+> whr nice wh
> $+$ returning nice rt
> <> statementEnd se
>
> statement _nice se ca (Truncate ann names ri casc) =
> annot ca ann <+>
> text "truncate"
> <+> sepCsvMap name names
> <+> text (case ri of
> RestartIdentity -> "restart identity"
> ContinueIdentity -> "continue identity")
> <+> cascade casc
> <> statementEnd se
>
>
>
> statement nice se ca (CreateTable ann tbl atts cns) =
> annot ca ann <+>
> text "create table"
> <+> name tbl <+> lparen
> $+$ nest 2 (vcat (csv (map attrDef atts ++ map (constraint nice) cns)))
> $+$ rparen <> statementEnd se
> where
> attrDef (AttributeDef _ n t def cons) =
> nmc n <+> typeName t
> <+> maybePrint (\e -> text "default" <+> scalExpr nice e) def
> <+> hsep (map cCons cons)
> 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 nice 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
>
> statement nice se ca (AlterTable ann nm act) =
> annot ca ann <+>
> text "alter table" <+> name nm
> <+> hcatCsvMap alterAction act <> statementEnd se
> where
> alterAction (AlterColumnDefault _ cnm def) =
> text "alter column" <+> nmc cnm
> <+> text "set default" <+> scalExpr nice def
> alterAction (AddConstraint _ con) =
> text "add " <+> constraint nice con
>
> statement _nice se ca (CreateSequence ann nm incr _ _ start cache) =
> annot ca ann <+>
> text "create sequence" <+> name nm <+>
> text "increment" <+> text (show incr) <+>
> text "no minvalue" <+>
> text "no maxvalue" <+>
> text "start" <+> text (show start) <+>
> text "cache" <+> text (show cache) <> statementEnd se
>
> statement _nice se ca (AlterSequence ann nm o) =
> annot ca ann <+>
> text "alter sequence" <+> name nm
> <+> text "owned by" <+> name o <> statementEnd se
>
> statement nice se ca (CreateTableAs ann t sel) =
> annot ca ann <+>
> text "create table"
> <+> name t <+> text "as"
> $+$ queryExpr nice True True Nothing sel
> <> statementEnd se
>
> statement nice 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 nice ca sts
> functionBody (PlpgsqlFnBody ann1 blk) =
> annot ca ann1 <+>
> statement nice True ca blk
> paramDefn (ParamDef _ n t) = nmc n <+> typeName t
> paramDefn (ParamDefTp _ t) = typeName t
>
> statement nice 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 nice ca sts
> $+$ text "end" <> statementEnd se
> where
> varDefn (VarDef _ n t v) =
> nmc n <+> typeName t
> <+> maybePrint (\x -> text ":=" <+> scalExpr nice 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 nice 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 nice True True Nothing sel) <> statementEnd se
>
> statement nice 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 nice e))
> cname = if n == ""
> then empty
> else text "constraint" <+> text n
>
> statement _nice 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 _nice 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")
> <+> ifExists ifE
> <+> sepCsvMap name names
> <+> cascade casc
> <> statementEnd se
>
> statement _nice 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 _nice se ca (CreateLanguage ann nm) =
> annot ca ann <+>
> text "create language" <+> text nm <> statementEnd se
>
> statement nice 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 nice) 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 _nice se ca (NullStatement ann) =
> annot ca ann <+> text "null" <> statementEnd se
> statement _nice se ca (ExitStatement ann lb) =
> annot ca ann <+> text "exit"
> <+> maybe empty text lb <> statementEnd se
>
> statement nice se ca (Into ann str is (QueryStatement _ q)) =
> annot ca ann <+>
> queryExpr nice True True (Just (str,is)) q <> statementEnd se
> statement nice se ca (Into ann str into st) =
> annot ca ann <+>
> statement nice False ca st
> <+> text "into"
> <> (if str
> then empty <+> text "strict"
> else empty)
> <+> sepCsvMap name into
> <> statementEnd se
>
> statement nice se ca (Assignment ann nm val) =
> annot ca ann <+>
> name nm <+> text ":=" <+> scalExpr nice val <> statementEnd se
>
> statement nice se ca (Return ann ex) =
> annot ca ann <+>
> text "return" <+> maybePrint (scalExpr nice) ex <> statementEnd se
>
> statement nice se ca (ReturnNext ann ex) =
> annot ca ann <+>
> text "return" <+> text "next" <+> scalExpr nice ex <> statementEnd se
>
> statement nice se ca (ReturnQuery ann sel) =
> annot ca ann <+>
> text "return" <+> text "query"
> <+> queryExpr nice True True Nothing sel <> statementEnd se
>
> statement nice 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 nice (StringLit emptyAnnotation st)
> <> ifNotEmpty (\e -> comma <+> csvExp nice e) exps
> <> statementEnd se
>
> statement nice se ca (ForQueryStatement ann lb i sel stmts) =
> annot ca ann <+>
> label lb <>
> text "for" <+> nmc i <+> text "in"
> <+> queryExpr nice True True Nothing sel <+> text "loop"
> $+$ nestedStatements nice ca stmts
> $+$ text "end loop" <> statementEnd se
>
> statement nice se ca (ForIntegerStatement ann lb var st en stmts) =
> annot ca ann <+>
> label lb <>
> text "for" <+> nmc var <+> text "in"
> <+> scalExpr nice st <+> text ".." <+> scalExpr nice en <+> text "loop"
> $+$ nestedStatements nice ca stmts
> $+$ text "end loop" <> statementEnd se
>
> statement nice se ca (WhileStatement ann lb ex stmts) =
> annot ca ann <+>
> label lb <>
> text "while" <+> scalExpr nice ex <+> text "loop"
> $+$ nestedStatements nice ca stmts
> $+$ text "end loop" <> statementEnd se
> statement nice se ca (LoopStatement ann lb stmts) =
> annot ca ann <+>
> label lb <>
> text "loop"
> $+$ nestedStatements nice ca stmts
> $+$ text "end loop" <> statementEnd se
>
> statement _nice se ca (ContinueStatement ann lb) =
> annot ca ann <+> text "continue"
> <+> maybe empty text lb <> statementEnd se
> statement nice se ca (Perform ann f@(FunCall _ _ _)) =
> annot ca ann <+>
> text "perform" <+> scalExpr nice f <> statementEnd se
> statement _ _ _ (Perform _ x) =
> error $ "internal error: statement not supported for " ++ show x
>
> statement _nice se ca (Copy ann tb cols src) =
> annot ca ann <+>
> text "copy" <+> name tb
> <+> ifNotEmpty (parens . sepCsvMap nmc) cols
> <+> text "from"
> <+> case src of
> CopyFilename s -> quotes $ text s <> statementEnd se
> Stdin -> text "stdin" <> text ";"
>
> statement _ _ ca (CopyData ann s) =
> annot ca ann <+>
> text s <> text "\\." <> newline
>
> statement nice se ca (If ann conds els) =
> annot ca ann <+>
> text "if" <+> constraintd (head conds)
> $+$ vcat (map (\c -> text "elseif" <+> constraintd c) $ tail conds)
> $+$ ifNotEmpty (\e -> text "else" $+$ nestedStatements nice ca e) els
> $+$ text "end if" <> statementEnd se
> where
> constraintd (ex, sts) = scalExpr nice ex <+> text "then"
> $+$ nestedStatements nice ca sts
> statement nice se ca (Execute ann s) =
> annot ca ann <+>
> text "execute" <+> scalExpr nice s <> statementEnd se
>
>
> statement nice se ca (CaseStatementSimple ann c conds els) =
> annot ca ann <+>
> text "case" <+> scalExpr nice c
> $+$ nest 2 (
> vcat (map (uncurry whenStatement) conds)
> $+$ elseStatement els
> ) $+$ text "end case" <> statementEnd se
> where
> whenStatement ex sts = text "when" <+> sepCsvMap (scalExpr nice) ex
> <+> text "then" $+$ nestedStatements nice ca sts
> elseStatement = ifNotEmpty (\s -> text "else"
> $+$ nestedStatements nice ca s)
> statement nice 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 nice) ex
> <+> text "then" $+$ nestedStatements nice ca sts
> elseStatement = ifNotEmpty (\s -> text "else"
> $+$ nestedStatements nice ca s)
>
>
>
> statement _nice se _ (Set _ n vs) =
> text "set" <+> text n <+> text "="
> <+> sepCsvMap (text . dv) vs <> statementEnd se
> where
> dv (SetStr _ s) = "'" ++ s ++ "'"
> dv (SetId _ i) = i
> dv (SetNum _ nm) = show nm
>
> statement _nice se _ (Notify _ n) =
> text "notify" <+> text n <> statementEnd se
>
> statementEnd :: Bool -> Doc
> statementEnd b = if b
> then semi <> newline
> else empty
-------------------------------------------------------------------------------
Statement components
>
>
> queryExpr :: Bool -> Bool -> Bool -> Maybe (Bool,[Name]) -> QueryExpr -> Doc
> queryExpr nice writeSelect _ intoi (Select _ dis l tb wh grp hav
> order lim off) =
> (text (if writeSelect then "select" else "")
> <+> (case dis of
> Dupes -> empty
> Distinct -> text "distinct"))
> $+$ nest 2 (vcat $ catMaybes
> [fmap (\(str,is) -> text "into"
> <+> (if str
> then text "strict"
> else empty)
> <+> sepCsvMap name is) intoi
> ,Just $ nest 2 $ selectList nice l
> ,Just $ if null tb
> then empty
> else text "from" $+$ nest 2 (sepCsvMap (tref nice) tb)
> ,Just $ whr nice wh
> ,case grp of
> [] -> Nothing
> g -> Just $ text "group by" $+$ nest 2 (sepCsvMap (scalExpr nice) g)
> ,flip fmap hav $ \h -> text "having" $+$ nest 2 (scalExpr nice h)
> ,Just $ orderBy nice order
> ,flip fmap lim $ \lm -> text "limit" <+> scalExpr nice lm
> ,flip fmap off $ \offs -> text "offset" <+> scalExpr nice offs
> ])
>
> queryExpr nice writeSelect topLev _ (CombineQueryExpr _ tp s1 s2) =
> let p = queryExpr nice writeSelect False Nothing s1
> $+$ (case tp of
> Except -> text "except"
> Union -> text "union"
> UnionAll -> text "union" <+> text "all"
> Intersect -> text "intersect")
> $+$ queryExpr nice True False Nothing s2
> in if topLev then p else parens p
> queryExpr nice _ _ _ (Values _ expss) =
> text "values" $$ nest 2 (vcat $ csv $ map (parens . csvExp nice) expss)
> queryExpr nice _ _ _ (WithQueryExpr _ wqs ex) =
> text "with" $$ nest 2 (vcat $ csv $ map pwq wqs)
> $+$ queryExpr nice 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 nice True False Nothing ex1)
> name :: Name -> Doc
> name (Name _ ns) = nmcs ns
> nmcs :: [NameComponent] -> Doc
> nmcs ns = hcat $ punctuate (text ".") $ map nmc ns
> nmc :: NameComponent -> Doc
> nmc (Nmc ns) = text ns
> nmc (QNmc ns) = doubleQuotes $ text ns
>
> tref :: Bool -> TableRef -> Doc
>
> tref nice (Tref _ f a) = name f <+> trefAlias nice a
> tref nice (JoinTref _ t1 nat jt t2 ex a) =
> parens (tref nice t1
> $+$ (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")
> <+> text "join"
> <+> tref nice t2
> <+> maybePrint (nest 2 . joinScalarExpr) ex
> <+> trefAlias nice a)
> where
> joinScalarExpr (JoinOn _ e) = text "on" <+> scalExpr nice e
> joinScalarExpr (JoinUsing _ ids) =
> text "using" <+> parens (sepCsvMap nmc ids)
>
> tref nice (SubTref _ sub alias) =
> parens (queryExpr nice True True Nothing sub)
> <+> text "as" <+> trefAlias nice alias
> tref nice (FunTref _ f@(FunCall _ _ _) a) = scalExpr nice f <+> trefAlias nice a
> tref _nice (FunTref _ x _) =
> error $ "internal error: node not supported in function tref: "
> ++ show x
>
> trefAlias :: Bool -> TableAlias -> Doc
> trefAlias _ (NoAlias _) = empty
> trefAlias _ (TableAlias _ t) = nmc t
>
>
> trefAlias nice (FullAlias _ t s) =
> nmc t <> (if nice
> then empty
> else parens (sepCsvMap nmc s))
> direction :: Direction -> Doc
> direction d = text $ case d of
> Asc -> "asc"
> Desc -> "desc"
>
> whr :: Bool -> Maybe ScalarExpr -> Doc
> whr nice (Just ex) = text "where" $+$ nest 2 (scalExpr nice ex)
> whr _ Nothing = empty
>
> selectList :: Bool -> SelectList -> Doc
> selectList nice (SelectList _ ex) =
> sepCsvMap selectItem ex
>
> where
>
> selectItem (SelectItem _ ex1@(QIdentifier _ is) nm) | nice, last is == nm = scalExprSl nice ex1
> selectItem (SelectItem _ ex1@(Identifier _ i) nm) | nice, i == nm = scalExprSl nice ex1
> selectItem (SelectItem _ ex1 nm) = scalExprSl nice ex1 <+> text "as" <+> nmc nm
> selectItem (SelExp _ e) = scalExprSl nice e
>
> cascade :: Cascade -> Doc
> cascade casc = text $ case casc of
> Cascade -> "cascade"
> Restrict -> "restrict"
>
>
> constraint :: Bool -> Constraint -> Doc
> constraint _nice (UniqueConstraint _ n c) =
> mname n <+> text "unique"
> <+> parens (sepCsvMap nmc c)
> constraint _nice (PrimaryKeyConstraint _ n p) =
> mname n <+>
> text "primary key"
> <+> parens (sepCsvMap nmc p)
> constraint nice (CheckConstraint _ n c) =
> mname n <+> text "check" <+> parens (scalExpr nice c)
> constraint _nice (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 :: Bool -> Maybe SelectList -> Doc
> returning nice l = case l of
> Nothing -> empty
> Just ls -> nest 2 (text "returning" <+> selectList nice ls)
>
> ifExists :: IfExists -> Doc
> ifExists i = case i of
> Require -> empty
> IfExists -> text "if exists"
>
>
>
> nestedStatements :: Bool -> (Annotation -> String) -> StatementList -> Doc
> nestedStatements nice pa = nest 2 . vcat . map (statement nice True pa)
>
> typeName :: TypeName -> Doc
> typeName (SimpleTypeName _ s) = text s
> typeName (PrecTypeName _ s i) = text s <> parens(integer i)
> typeName (Prec2TypeName _ s i i1) = text s <> parens (sepCsv [integer i, integer i1])
> typeName (ArrayTypeName _ t) = typeName t <> text "[]"
> typeName (SetOfTypeName _ t) = text "setof" <+> typeName t
>
>
>
> scalExpr :: Bool -> ScalarExpr -> Doc
> scalExpr _ (AntiScalarExpr s) = text $ "$(" ++ s ++ ")"
> scalExpr _ (Star _) = text "*"
> scalExpr _ (QStar _ i) = nmc i <> text ".*"
> scalExpr _ (Identifier _ i) = nmc i
>
> scalExpr _nice (QIdentifier _a [i1, i]) = parens (nmc i1) <> text "." <> nmc i
> scalExpr _nice (QIdentifier _a _) = error "only supports 2 part qualified identifers atm"
>
>
> scalExpr _ (NumberLit _ n) = text n
> scalExpr _ (StringLit _ s) =
> text "'" <> text replaceQuotes <> text "'"
> where
> replaceQuotes = replace "'" "''" s
>
> scalExpr nice (FunCall _ n es) =
>
> case getTName n of
> Just "!and" | nice, [a,b] <- es -> doLeftAnds a b
> Just "!arrayctor" -> text "array" <> brackets (csvExp nice es)
> Just "!between" -> scalExpr nice (head es) <+> text "between"
> <+> parens (scalExpr nice (es !! 1))
> <+> text "and"
> <+> parens (scalExpr nice (es !! 2))
> Just "!substring" -> text "substring"
> <> parens (scalExpr nice (head es)
> <+> text "from" <+> scalExpr nice (es !! 1)
> <+> text "for" <+> scalExpr nice (es !! 2))
> Just "!arraysub" -> case es of
> (Identifier _ i : es1) -> nmc i
> <> brackets (csvExp nice es1)
> _ -> parens (scalExpr nice (head es))
> <> brackets (csvExp nice (tail es))
> Just "!rowctor" -> text "row" <> parens (sepCsvMap (scalExpr nice) es)
> Just "."
>
> | [a,b] <- es -> parens (scalExpr nice a) <> text "." <> scalExpr nice b
> Just n' | isOperatorName n' ->
> case forceRight (getOperatorType defaultTemplate1Catalog n') of
> BinaryOp ->
> let e1d = scalExpr nice (head es)
> opd = text $ filterKeyword n'
> e2d = scalExpr nice (es !! 1)
> in parens (if n' `elem` ["!and", "!or"]
> then vcat [e1d, opd <+> e2d]
> else e1d <+> opd <+> e2d)
> PrefixOp -> parens (text (if n' == "u-"
> then "-"
> else filterKeyword n')
> <+> parens (scalExpr nice (head es)))
> PostfixOp -> parens (scalExpr nice (head es)
> <+> text (filterKeyword n'))
> _ -> name n <> parens (csvExp nice es)
> where
> filterKeyword t = case t of
> "!and" -> "and"
> "!or" -> "or"
> "!not" -> "not"
> "!isnull" -> "is null"
> "!isnotnull" -> "is not null"
> "!like" -> "like"
> "!notlike" -> "not like"
> x -> x
>
> doLeftAnds a b = let as = and' a
> in vcat ((scalExpr nice (head as)
> : map (\x -> text "and" <+> scalExpr nice x) (tail as))
> ++ [text "and" <+> scalExpr nice b])
> and' a = case a of
> FunCall _ f [x,y] | Just "!and" <- getTName f -> and' x ++ and' y
> _ -> [a]
>
> scalExpr _ (BooleanLit _ b) = bool b
> scalExpr nice (InPredicate _ att t lst) =
> scalExpr nice att <+> (if not t then text "not" else empty) <+> text "in"
> <+> parens (case lst of
> InList _ expr -> csvExp nice expr
> InQueryExpr _ sel -> queryExpr nice True True Nothing sel)
> scalExpr nice (LiftOperator _ op flav args) =
> scalExpr nice (head args) <+> text op
> <+> text (case flav of
> LiftAny -> "any"
> LiftAll -> "all")
> <+> parens (scalExpr nice $ head $ tail args)
> scalExpr nice (ScalarSubQuery _ s) = parens (queryExpr nice True True Nothing s)
> scalExpr _ (NullLit _) = text "null"
> scalExpr nice (WindowFn _ fn part order frm) =
> scalExpr nice fn <+> text "over"
> <+> parens (if hp || ho
> then (if hp
> then text "partition by" <+> csvExp nice part
> else empty)
> <+> orderBy nice order
> <+> frameStuff
> else empty)
> where
> hp = not (null part)
> ho = not (null order)
> frameStuff = case frm of
> FrameUnboundedPreceding -> text "range unbounded preceding"
> FrameUnboundedFull -> text "range between unbounded preceding and unbounded following"
> FrameRowsUnboundedPreceding -> text "rows unbounded preceding"
>
> scalExpr nice (AggregateFn _ d (FunCall _ fn es) o) =
> name fn <> parens ((case d of
> Dupes -> text "all"
> Distinct -> text "distinct")
> <+> csvExp nice es
> <+> orderBy nice o)
> scalExpr _ (AggregateFn _ _ _ _) = error "bad syntax for aggregate function"
> scalExpr nice (Case _ whens els) =
> text "case"
> $+$ nest 2 (vcat (map whn whens)
> $+$ maybePrint (\e -> text "else" <+> scalExpr nice e) els)
> $+$ text "end"
> where
> whn (ex1, ex2) =
> text "when" <+> sepCsvMap (scalExpr nice) ex1
> <+> text "then" <+> scalExpr nice ex2
>
> scalExpr nice (CaseSimple _ val whens els) =
> text "case" <+> scalExpr nice val
> $+$ nest 2 (vcat (map whn whens)
> $+$ maybePrint (\e -> text "else" <+> scalExpr nice e) els)
> $+$ text "end"
> where
> whn (ex1, ex2) =
> text "when" <+> sepCsvMap (scalExpr nice) ex1
> <+> text "then" <+> scalExpr nice ex2
>
> scalExpr _ (PositionalArg _ a) = text "$" <> integer a
> scalExpr _ (Placeholder _) = text "?"
> scalExpr nice (Exists _ s) =
> text "exists" <+> parens (queryExpr nice True True Nothing s)
> scalExpr nice (Cast _ ex t) = text "cast" <> parens (scalExpr nice ex
> <+> text "as"
> <+> typeName t)
> scalExpr nice (TypedStringLit a t s) =
> typeName t <+> scalExpr nice (StringLit a s)
> scalExpr nice (Interval a v f p) =
> text "interval" <+> scalExpr nice (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 nice (Extract _ f e) =
> text "extract"
> <> parens (text field <+> text "from" <+> scalExpr nice 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"
> scalExprSl :: Bool -> ScalarExpr -> Doc
> scalExprSl nice (FunCall _ f es) | Just "." <- getTName f
> , [a@(Identifier _ _), b] <- es =
> parens (scalExprSl nice a) <> text "." <> scalExprSl nice b
> scalExprSl nice x = scalExpr nice x
>
> set :: Bool -> SetClause -> Doc
> set nice (SetClause _ a e) =
>
> nmc a <+> text "=" <+> scalExpr nice e
>
> set nice (MultiSetClause _ is (FunCall _ f es)) | Just "!rowctor" <- getTName f =
> parens (sepCsvMap nmc is) <+> text "="
> <+> parens (sepCsvMap (scalExpr nice) es)
> set _ a = error $ "bad expression in set in update: " ++ show a
>
> --utils
>
>
>
> csvExp :: Bool -> [ScalarExpr] -> Doc
> csvExp nice = hcatCsvMap (scalExpr nice)
>
> 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 :: Bool -> [(ScalarExpr,Direction)] -> Doc
> orderBy _ [] = empty
> orderBy nice os =
> text "order by"
> $+$ nest 2 (sepCsvMap (\(oe,od) -> scalExpr nice oe
> <+> direction od) 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