packages feed

hssqlppp-0.2.0: Database/HsSqlPpp/AstInternals/TypeChecking/SelectStatement.ag

{-
Copyright 2009 Jake Wheat

= basic select statements

This is a bit of a mess, will be rewritten with a proper literate
flavour once all the different bits are type checking ok, which should
make it much more readable.

-}

SEM Statement
    | SelectStatement
        loc.tpe = dependsOnRTpe [getTypeAnnotation @ex.annotatedTree] $ Right $ Pseudo Void
        loc.statementType = [StatementType (getPlaceholderTypes @ex.annotatedTree) $ leftToEmpty id  $
                                                unwrapSetOfComposite $
                                                getTypeAnnotation @ex.annotatedTree]
        loc.backTree = SelectStatement @ann @ex.annotatedTree
        loc.catUpdates = []

SEM SelectExpression
    | Values Select CombineSelect
        lhs.annotatedTree = annTypesAndErrors @loc.backTree
                              (tpeToT @loc.tpe)
                              (getErrors @loc.tpe)
                              Nothing
{-

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

Type checking select expressions

The main issue is the complicated flow of identifier bindings through the
various parts. This is the rough order in which this happens:

with
from
where
groupby
having
select
combine
orderby
limit

if a type error occurs, we want to give up on any following stages,
rather than create loads of type errors (maybe this could be refined
more).

The select list produces the final type which the selectexpression has.

inside the from, if we have any join expressions we need to pass the types
from the joined trefs to the join expressions.

So, the basic plan is to propagate the iden bindings in the cat attribute
as elsewhere, and also pass along a flag to say whether the previous stage
type checked or not, so we can bail if it has failed.

alternative idea: explore transforming the ast for a select expression into
something using relational algebra-like operations - can then follow the flow
of ids easily. The problem might be with updating the annotations in the original
tree though.

== cat passing current bodge

cat flow:
current simple version:
from tref -> select list
          -> where

(so we take the identifiers and types from the tref part, and send
them into the selectlist and where parts)

full order of identifier passing:
   1. from
   2. where
   3. group by
   4. having
   5. select


group by notes, from the pg manual:
group by expressions can be an input column name, or the name or
ordinal number of an output column (SELECT list item), or an arbitrary
expression formed from input-column values. In case of ambiguity, a
GROUP BY name will be interpreted as an input-column name rather than
an output column name.

For now, just send the input columns in as identifiers
-}

SEM SelectExpression
    | Select
         loc.newLib = case updateBindings @lhs.lib @lhs.cat @selTref.libUpdates of
                        Left x -> error $ show x -- @lhs.cat
                        Right e -> e
         selSelectList.lib = @loc.newLib
         selWhere.lib = @loc.newLib
         selGroupBy.lib = @loc.newLib
         selOrderBy.lib = @loc.newLib



SEM SelectExpression
    | Values
        loc.tpe = typeCheckValuesExpr
                              @lhs.cat
                              @vll.typeListList
        loc.backTree = Values @ann @vll.annotatedTree
    | Select
        loc.tpe =
           do
           --let trefType = fromMaybe typeBool $ fmap getTypeAnnotation
           --                                         @selTref.annotatedTree
           Right $ case @selSelectList.listType of
                     [(_,Pseudo Void)] -> Pseudo Void
                     _ -> SetOfType $ CompositeType @selSelectList.listType
        loc.backTree = Select @ann
                              @selDistinct.annotatedTree
                              @selSelectList.annotatedTree
                              @selTref.annotatedTree
                              @selWhere.annotatedTree
                              @selGroupBy.annotatedTree
                              @selHaving.annotatedTree
                              @selOrderBy.annotatedTree
                              @selLimit.annotatedTree
                              @selOffset.annotatedTree
    | CombineSelect
        loc.tpe =
          let sel1t = getTypeAnnotation @sel1.annotatedTree
              sel2t = getTypeAnnotation @sel2.annotatedTree
          in dependsOnRTpe [sel1t, sel2t] $
                typeCheckCombineSelect @lhs.cat sel1t sel2t
        loc.backTree = CombineSelect @ann @ctype.annotatedTree
                                     @sel1.annotatedTree
                                     @sel2.annotatedTree

{

typeCheckValuesExpr :: Catalog -> [[Type]] -> Either [TypeError] Type
typeCheckValuesExpr cat rowsTs =
        let colNames = zipWith (++)
                           (repeat "column")
                           (map show [1..length $ head rowsTs])
        in unionRelTypes cat rowsTs colNames


typeCheckCombineSelect :: Catalog -> Type -> Type -> Either [TypeError] Type
typeCheckCombineSelect cat v1 v2 = do
    u1 <- unwrapSetOfComposite v1
    let colNames = map fst u1
    u2 <- unwrapSetOfComposite v2
    let colTypes1 = map snd u1
    let colTypes2 = map snd u2
    unionRelTypes cat [colTypes1,colTypes2] colNames

unionRelTypes :: Catalog -> [[Type]] -> [String] -> Either [TypeError] Type
unionRelTypes cat rowsTs colNames =
  let lengths = map length rowsTs
  in case () of
             _ | null rowsTs ->
                   Left [NoRowsGivenForValues]
               | not (all (==head lengths) lengths) ->
                   Left [ValuesListsMustBeSameLength]
               | otherwise ->
                   --i don't think this propagates all the errors, just the first set
                   mapM (resolveResultSetType cat) (transpose rowsTs) >>=
                     (return . SetOfType . CompositeType . zip colNames)

}

INCLUDE "TypeChecking/TableRefs.ag"

INCLUDE "TypeChecking/SelectLists.ag"