packages feed

hssqlppp-0.0.5: Database/HsSqlPpp/Ast.ag

{-
Copyright 2009 Jake Wheat

This file contains the ast nodes, and the api functions to pass an ast
and get back type information.

It uses the Utrecht University Attribute Grammar system:

http://www.cs.uu.nl/wiki/bin/view/HUT/AttributeGrammarSystem
http://www.haskell.org/haskellwiki/The_Monad.Reader/Issue4/Why_Attribute_Grammars_Matter

The attr and sem definitions are in TypeChecking.ag, which is included
into this file.

These ast nodes are both used as the result of successful parsing, and
as the input to the type checker and the pretty printer.

= compiling

use

uuagc -dcfws Ast.ag

to generate a new Ast.hs from this file

(install uuagc with
cabal install uuagc
)

-}

MODULE {Database.HsSqlPpp.Ast}
{
    -- exports
    MySourcePos

    --ast nodes
   ,Statement (..)
   ,SelectExpression (..)
   ,FnBody (..)
   ,SetClause (..)
   ,TableRef (..)
   ,JoinExpression (..)
   ,JoinType (..)
   ,SelectList (..)
   ,SelectItem (..)
   ,CopySource (..)
   ,AttributeDef (..)
   ,RowConstraint (..)
   ,Constraint (..)
   ,TypeAttributeDef (..)
   ,ParamDef (..)
   ,VarDef (..)
   ,RaiseType (..)
   ,CombineType (..)
   ,Volatility (..)
   ,Language (..)
   ,TypeName (..)
   ,DropType (..)
   ,Cascade (..)
   ,Direction (..)
   ,Distinct (..)
   ,Natural (..)
   ,IfExists (..)
   ,RestartIdentity (..)
   ,Expression (..)
   ,OperatorType (..)
   ,getOperatorType
   ,InList (..)
   ,StatementList
   --checking stuff
   ,Message (..)
   ,MessageStuff (..)
   --types
   ,Type (..)
   ,PseudoType (..)
   ,TypeErrorInfo (..)
   ,StatementInfo (..)
   --scope
   ,Scope(..)
   ,defaultScope
   ,emptyScope
   --fns
   ,checkAst
   ,getExpressionType
   ,getStatementsType
   ,getStatementsTypeScope
   ,getStatementsInfo
   ,getStatementsInfoScope
   ,resetSps
   ,resetSp
   ,resetSp'
   ,resetSps'
   ,nsp
   --forward some defs
   ,typeSmallInt
   ,typeBigInt
   ,typeInt
   ,typeNumeric
   ,typeFloat4
   ,typeFloat8
   ,typeVarChar
   ,typeChar
   ,typeBool

}
{
import Data.Maybe
import Data.List
import Debug.Trace
import Control.Monad.Error
import Control.Arrow

import Database.HsSqlPpp.TypeType
import Database.HsSqlPpp.AstUtils
import Database.HsSqlPpp.TypeConversion
import Database.HsSqlPpp.TypeCheckingH
import Database.HsSqlPpp.Scope
import Database.HsSqlPpp.DefaultScope
}

{-
================================================================================

SQL top level statements

everything is chucked in here: dml, ddl, plpgsql statements

-}

DATA Statement

--queries

    | SelectStatement ex:SelectExpression

-- dml

    --table targetcolumns insertdata(values or select statement) returning
    | Insert table : String
             targetCols : StringList
             insData : SelectExpression
             returning : (Maybe SelectList)
    --tablename setitems where returning
    | Update table : String
             assigns : SetClauseList
             whr : Where
             returning : (Maybe SelectList)
    --tablename, where, returning
    | Delete table : String
             whr : Where
             returning : (Maybe SelectList)
    --tablename column names, from
    | Copy table : String
           targetCols : StringList
           source : CopySource
    --represents inline data for copy statement
    | CopyData insData : String
    | Truncate tables: StringList
               restartIdentity : RestartIdentity
               cascade : Cascade

-- ddl

    | CreateTable name : String
                  atts : AttributeDefList
                  cons : ConstraintList
    | CreateTableAs name : String
                    expr : SelectExpression
    | CreateView name : String
                 expr : SelectExpression
    | CreateType name : String
                 atts : TypeAttributeDefList
    -- language name args rettype bodyquoteused body vol
    | CreateFunction lang : Language
                     name : String
                     params : ParamDefList
                     rettype : TypeName
                     bodyQuote : String
                     body : FnBody
                     vol : Volatility
    -- name type checkexpression
    | CreateDomain name : String
                   typ : TypeName
                   check : (Maybe Expression)
    -- ifexists (name,argtypes)* cascadeorrestrict
    | DropFunction ifE : IfExists
                   sigs : StringStringListPairList
                   cascade : Cascade
    -- ifexists names cascadeorrestrict
    | DropSomething dropType : DropType
                    ifE : IfExists
                    names : StringList
                    cascade : Cascade
    | Assignment target : String
                 value : Expression
    | Return value : (Maybe Expression)
    | ReturnNext expr : Expression
    | ReturnQuery sel : SelectExpression
    | Raise level : RaiseType
            message : String
            args : ExpressionList
    | NullStatement
    | Perform expr : Expression
    | Execute expr : Expression
    | ExecuteInto expr : Expression
                  targets : StringList
    | ForSelectStatement var : String
                         sel : SelectExpression
                         sts : StatementList
    | ForIntegerStatement var : String
                          from : Expression
                          to : Expression
                          sts : StatementList
    | WhileStatement expr : Expression
                     sts : StatementList
    | ContinueStatement
    --variable, list of when parts, else part
    | CaseStatement val : Expression
                    cases : ExpressionListStatementListPairList
                    els : StatementList
    --list is
    --first if (condition, statements):elseifs(condition, statements)
    --last bit is else statements
    | If cases : ExpressionStatementListPairList
         els : StatementList

-- =============================================================================

--Statement components

-- maybe this should be called relation valued expression?
DATA SelectExpression
    | Select selDistinct : Distinct
             selSelectList : SelectList
             selTref : MTableRef
             selWhere : Where
             selGroupBy : ExpressionList
             selHaving : (Maybe Expression)
             selOrderBy : ExpressionList
             selDir : Direction
             selLimit : (Maybe Expression)
             selOffset : (Maybe Expression)
    | CombineSelect ctype : CombineType
                    sel1 : SelectExpression
                    sel2 : SelectExpression
    | Values vll:ExpressionListList

TYPE MTableRef = MAYBE TableRef
TYPE Where = MAYBE Expression

DATA FnBody | SqlFnBody sts : StatementList
            | PlpgsqlFnBody VarDefList sts : StatementList

DATA SetClause | SetClause att:String val:Expression
               | RowSetClause atts:StringList vals:ExpressionList

DATA TableRef | Tref tbl:String
              | TrefAlias tbl : String
                          alias : String
              | JoinedTref tbl : TableRef
                           nat : Natural
                           joinType : JoinType
                           tbl1 : TableRef
                           onExpr : OnExpr
              | SubTref sel : SelectExpression alias : String
              | TrefFun fn:Expression
              | TrefFunAlias fn:Expression alias:String

TYPE OnExpr = MAYBE JoinExpression

DATA JoinExpression | JoinOn Expression | JoinUsing StringList

DATA JoinType | Inner | LeftOuter| RightOuter | FullOuter | Cross

-- select columns, into columns

DATA SelectList | SelectList items:SelectItemList StringList

DATA SelectItem | SelExp ex:Expression
                | SelectItem ex:Expression name:String

DATA CopySource | CopyFilename String | Stdin

--name type default null constraint

DATA AttributeDef | AttributeDef name : String
                                 typ : TypeName
                                 check : (Maybe Expression)
                                 cons : RowConstraintList

--Constraints which appear attached to an individual field

DATA RowConstraint | NullConstraint
                   | NotNullConstraint
                   | RowCheckConstraint Expression
                   | RowUniqueConstraint
                   | RowPrimaryKeyConstraint
                   | RowReferenceConstraint table : String
                                            att : (Maybe String)
                                            onUpdate : Cascade
                                            onDelete : Cascade

--constraints which appear on a separate row in the create table

DATA Constraint | UniqueConstraint StringList
                | PrimaryKeyConstraint StringList
                | CheckConstraint Expression
                  -- sourcecols targettable targetcols ondelete onupdate
                | ReferenceConstraint atts : StringList
                                      table : String
                                      tableAtts : StringList
                                      onUpdate : Cascade
                                      onDelete : Cascade

DATA TypeAttributeDef | TypeAttDef name : String
                                   typ : TypeName

DATA ParamDef | ParamDef name:String typ:TypeName
              | ParamDefTp typ:TypeName

DATA VarDef | VarDef name : String
                     typ : TypeName
                     value : (Maybe Expression)

DATA RaiseType | RNotice | RException | RError

DATA CombineType | Except | Union | Intersect | UnionAll

DATA Volatility | Volatile | Stable | Immutable

DATA Language | Sql | Plpgsql

DATA TypeName | SimpleTypeName tn:String
              | PrecTypeName tn:String prec:Integer
              | ArrayTypeName typ:TypeName
              | SetOfTypeName typ:TypeName

DATA DropType | Table
              | Domain
              | View
              | Type

DATA Cascade | Cascade | Restrict

DATA Direction | Asc | Desc

DATA Distinct | Distinct | Dupes

DATA Natural | Natural | Unnatural

DATA IfExists | Require | IfExists

DATA RestartIdentity | RestartIdentity | ContinueIdentity

{-
================================================================================

Expressions

Similarly to the statement type, all expressions are chucked into one
even though there are many restrictions on which expressions can
appear in different places.  Maybe this should be called scalar
expression?

-}
DATA Expression | IntegerLit Integer
                | FloatLit Double
                | StringLit quote : String
                            value : String
                | NullLit
                | BooleanLit Bool
                | PositionalArg Integer
                | Cast expr:Expression tn:TypeName
                | Identifier i:String
                | Case cases : CaseExpressionListExpressionPairList
                       els : MaybeExpression
                | CaseSimple value : Expression
                       cases : CaseExpressionListExpressionPairList
                       els : MaybeExpression
                | Exists sel : SelectExpression
                | FunCall funName:String args:ExpressionList
                | InPredicate expr:Expression i:Bool list:InList
                  -- windowfn selectitem partitionby orderby orderbyasc?
                | WindowFn fn : Expression
                           partitionBy : ExpressionList
                           orderBy : ExpressionList
                           dir : Direction
                | ScalarSubQuery sel : SelectExpression

TYPE MaybeExpression = MAYBE Expression

{-

list of expression flavours from postgresql with the equivalents in this ast
pg                                here
--                                ----
constant/literal                  integerlit, floatlit, unknownstringlit, nulllit, boollit
column reference                  identifier
positional parameter reference    positionalarg
subscripted expression            funcall
field selection expression        identifier
operator invocation               funcall
function call                     funcall
aggregate expression              funcall
window function call              windowfn
type cast                         cast
scalar subquery                   scalarsubquery
array constructor                 funcall
row constructor                   funall

Anything that is represented in the ast as some sort of name plus a
list of expressions as arguments is treated as the same type of node:
FunCall.

This includes
symbol operators
regular function calls
keyword operators e.g. and, like (ones which can be parsed as normal
  syntactic operators)
unusual syntax operators, e.g. between
unusual syntax function calls e.g. substring(x from 5 for 3)
arrayctors e.g. array[3,5,6]
rowctors e.g. ROW (2,4,6)
array subscripting

list of keyword operators (regular prefix, infix and postfix):
and, or, not
is null, is not null, isnull, notnull
is distinct from, is not distinct from
is true, is not true,is false, is not false, is unknown, is not unknown
like, not like, ilike, not ilike
similar to, not similar to
in, not in (don't include these here since the argument isn't always an expr)

unusual syntax operators and fn calls
between, not between, between symmetric
overlay, substring, trim
any, some, all

Most of unusual syntax forms and keywords operators are not yet
supported, so this is mainly a todo list.

Keyword operators are encoded with the function name as a ! followed
by a string
e.g.
operator 'and' -> FunCall "!and" ...
see keywordOperatorTypes value in AstUtils.lhs for the list of
currently supported keyword operators.

-}



DATA InList | InList exprs : ExpressionList
            | InSelect sel : SelectExpression

-- some list nodes, not sure if all of these are needed as separately
-- named node types

TYPE ExpressionList = [Expression]
TYPE ExpressionListList = [ExpressionList]
TYPE StringList = [String]
TYPE SetClauseList = [SetClause]
TYPE AttributeDefList = [AttributeDef]
TYPE ConstraintList = [Constraint]
TYPE TypeAttributeDefList = [TypeAttributeDef]
TYPE ParamDefList = [ParamDef]
TYPE StringStringListPair = (String,StringList)
TYPE StringStringListPairList = [StringStringListPair]
TYPE ExpressionListStatementListPair = (ExpressionList,StatementList)
TYPE ExpressionListStatementListPairList = [ExpressionListStatementListPair]
TYPE ExpressionStatementListPair = (Expression, StatementList)
TYPE ExpressionStatementListPairList = [ExpressionStatementListPair]
TYPE VarDefList = [VarDef]
TYPE SelectItemList = [SelectItem]
TYPE RowConstraintList = [RowConstraint]
TYPE CaseExpressionListExpressionPair = (CaseExpressionList,Expression)
TYPE CaseExpressionList = [Expression]
TYPE CaseExpressionListExpressionPairList = [CaseExpressionListExpressionPair]

{-

slightly hacky, add the source positions only in statement lists this
includes top level statements, and statements inside createfunction,
and nested inside if, while, case statements, and the like, but
unfortunately not select expressions inside other
expressions/statements, so we get particularly crap source positions
for big select statements.

Was done like this for expediency.

This will be changed back to StatementList = [Statement] when all the
nodes have sourcepositioning information added to them, doing which
has been put off for no good reason.

-}

TYPE SourcePosStatement = (MySourcePos, Statement)
TYPE StatementList = [SourcePosStatement]



-- Add a root data type so we can put initial values for inherited
-- attributes in the section which defines and uses those attributes
-- rather than in the sem_ calls

DATA Root | Root statements:StatementList
DERIVING Root: Show

-- use an expression root also to support type checking,
-- etc., individual expressions

DATA ExpressionRoot | ExpressionRoot expr:Expression
DERIVING ExpressionRoot: Show

{-
================================================================================

=some basic bookkeeping

attributes which every node has
-}

SET AllNodes = Statement SelectExpression FnBody SetClause TableRef
               JoinExpression JoinType
               SelectList SelectItem CopySource AttributeDef RowConstraint
               Constraint TypeAttributeDef ParamDef VarDef RaiseType
               CombineType Volatility Language TypeName DropType Cascade
               Direction Distinct Natural IfExists RestartIdentity
               Expression InList MaybeExpression
               ExpressionList ExpressionListList StringList SetClauseList
               AttributeDefList ConstraintList TypeAttributeDefList
               ParamDefList StringStringListPair StringStringListPairList
               StatementList ExpressionListStatementListPair
               ExpressionListStatementListPairList ExpressionStatementListPair
               ExpressionStatementListPairList VarDefList SelectItemList
               RowConstraintList CaseExpressionListExpressionPair
               CaseExpressionListExpressionPairList CaseExpressionList
               SourcePosStatement MTableRef TableRef OnExpr Where


SET NonListNodes = Statement SelectExpression FnBody SetClause TableRef
                   JoinExpression JoinType
                   SelectItem CopySource AttributeDef RowConstraint
                   Constraint TypeAttributeDef ParamDef VarDef RaiseType
                   CombineType Volatility Language TypeName DropType Cascade
                   Direction Distinct Natural IfExists RestartIdentity
                   Expression InList MaybeExpression
                   StringStringListPair ExpressionListStatementListPair
                   ExpressionStatementListPair
                   SourcePosStatement MTableRef OnExpr Where


SET ListNodes = SelectList ExpressionList ExpressionListList StringList
                SetClauseList
                AttributeDefList ConstraintList TypeAttributeDefList
                ParamDefList StringStringListPairList
                StatementList
                ExpressionListStatementListPairList
                ExpressionStatementListPairList VarDefList SelectItemList
                RowConstraintList
                CaseExpressionListExpressionPairList CaseExpressionList
                CaseExpressionListExpressionPair

DERIVING AllNodes: Show,Eq

INCLUDE "TypeChecking.ag"

{-

================================================================================

used to use record syntax to try to insulate code from field changes,
and not have to write out loads of nothings and [] for simple selects,
but don't know how to create haskell named records from uuagc DATA
things

makeSelect :: Statement
makeSelect = Select Dupes (SelectList [SelExp (Identifier "*")] [])
                   Nothing Nothing [] Nothing [] Asc Nothing Nothing


================================================================================

= checkAst

test function to run on asts, returns a list of errors, warnings, etc.
bit stale

-}
{
checkAst :: StatementList -> [Message]
checkAst sts = let t = sem_Root (Root sts)
               in (messages_Syn_Root (wrap_Root t Inh_Root {scope_Inh_Root = defaultScope}))
{-
================================================================================

= Types

These are the utility functions which clients use to typecheck sql.

-}

getExpressionType :: Scope -> Expression -> Type
getExpressionType scope ex =
    let t = sem_ExpressionRoot (ExpressionRoot ex)
    in (nodeType_Syn_ExpressionRoot
        (wrap_ExpressionRoot t Inh_ExpressionRoot {scope_Inh_ExpressionRoot = combineScopes defaultScope scope}))


getStatementsType :: StatementList -> [Type]
getStatementsType = getStatementsTypeScope emptyScope

getStatementsTypeScope :: Scope -> StatementList -> [Type]
getStatementsTypeScope scope st =
    let t = sem_Root (Root st)
        ta = wrap_Root t Inh_Root {scope_Inh_Root = combineScopes defaultScope scope}
        tl = nodeType_Syn_Root ta
    in (unwrapTypeList tl)

getStatementsInfo :: StatementList -> [StatementInfo]
getStatementsInfo = getStatementsInfoScope emptyScope

getStatementsInfoScope :: Scope -> StatementList -> [StatementInfo]
getStatementsInfoScope scope st =
    let t = sem_Root (Root st)
        ta = wrap_Root t Inh_Root {scope_Inh_Root = combineScopes defaultScope scope}
        t2 = statementInfo_Syn_Root ta
    in t2


--hack job, often not interested in the source positions when testing
--the asts produced, so this function will reset all the source
--positions to empty ("", 0, 0) so we can compare them for equality, etc.
--without having to get the positions correct.

resetSps :: [Statement] -> [Statement]
resetSps = map resetSp

resetSp :: Statement -> Statement
resetSp (CreateFunction l n p r bq b v) = CreateFunction l n p r bq
                                              (case b of
                                                SqlFnBody stss -> SqlFnBody (map resetSp' stss)
                                                PlpgsqlFnBody vd stss -> PlpgsqlFnBody vd (map resetSp' stss))
                                            v
resetSp (ForSelectStatement v s stss) = ForSelectStatement v s (map resetSp' stss)
resetSp (ForIntegerStatement v f t stss) = ForIntegerStatement v f t (map resetSp' stss)
resetSp (CaseStatement v cs els) = CaseStatement v (map (second (map resetSp')) cs) (map resetSp' els)
resetSp (If cs els) = If (map (second (map resetSp')) cs) (map resetSp' els)
resetSp a = a

resetSp' :: SourcePosStatement -> SourcePosStatement
resetSp' (_,st) = (nsp,resetSp st)

resetSps' :: StatementList -> StatementList
resetSps' = map resetSp'

nsp :: MySourcePos
nsp = ("", 0,0)
}

{-

Future plans:

Investigate how much mileage can get out of making these nodes the
parse tree nodes, and using a separate ast. Hinges on how much extra
value can get from making the types more restrictive for the ast nodes
compared to the parse tree.

Would like to turn this back into regular Haskell file, maybe could
use AspectAG instead of uuagc to make this happen?


-}