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"