> {- | Functions to convert sql asts to valid SQL source code.
>
>    Some effort is made produce human readable output.
> -}
> {-# LANGUAGE PatternGuards,OverloadedStrings,TypeSynonymInstances,FlexibleInstances #-}
> module Database.HsSqlPpp.Internals.PrettyInternal
>     (--convert a sql ast to text
>      prettyStatements
>     --,printStatementsAnn
>     ,prettyQueryExpr
>      --convert a single expression parse node to text
>     ,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.Utils.Utils

> 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 Database.HsSqlPpp.Internals.StringLike
> 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)

> -- | Convert an ast back to valid SQL source.
> prettyStatements :: PrettyFlags -> [Statement] -> L.Text
> prettyStatements f = prettyStatementsAnn f (const "")
>
> -- | Convert the ast back to valid source, and convert any annotations to
> -- text using the function provided and interpolate the output of
> -- this function (inside comments) with the SQL source.

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"

> -- | pretty print a query expression
> prettyQueryExpr :: PrettyFlags -> QueryExpr -> L.Text
> prettyQueryExpr f ast = renderText (queryExpr f True True Nothing ast <> statementEnd True)

> -- | pretty print a scalar expression
> 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

> -- Statements
>
> statement :: PrettyFlags -> Bool -> (Annotation -> String) -> Statement -> Doc
> statement _flg _se _ca (AntiStatement s) = text $ "$(" ++ s ++ ")"
>
> -- selects
>
> statement flg se ca (QueryStatement ann s) =
>   annot ca ann <+>
>   queryExpr flg True True Nothing s <> statementEnd se
>
> --dml
>
> 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
>
> -- ddl
>
> 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
>
> -- plpgsql
>
> 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
>   --fixme, should be insert,update,delete,execute

> 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) =
>    -- todo: don't use error
>    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
>                           -- put statement end without new line
>                           -- so that the copydata follows immediately after
>                           -- without an extra blank line inbetween
>                           <> 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

>
> -- misc
>
> 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

> -- selects
>
> 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) =
>       -- todo: don't use error
>       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
> -- hack this out for now. When the type checking is fixed, can try
> -- to eliminate unneeded aliases?
> 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 flg t@(JoinTref {}) = parens $ tref flg t
> 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
> -- ddl
>
> 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"
>            -- _ -> "unknown type " ++ (show x)

> 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)

> -- plpgsql
>
> 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"
>
> -- expressions
>
> 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 _)) =
>     -- todo: don't use error
>     error "Antiname component"

> scalExpr _flg (Identifier _a (Name _ is)) =
>   hcat (punctuate (text ".") (map nmc is))


> scalExpr _ (NumberLit _ n) = ttext n
> scalExpr _ (StringLit _ s) = -- needs some thought about using $$?
>                           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 "."   -- special case to avoid ws around '.'. Don't know if this is important
>                 -- or just cosmetic
>          -> 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
>
> --utils
>
> -- convert a list of expressions to horizontal csv
>
> 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)

> --vcatCsvMap :: (a -> Doc) -> [a] -> Doc
> --vcatCsvMap ex = vcat . csv . map ex

>
> 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