diff --git a/src/lib/ProjectM36/DataTypes/SQL/Null.hs b/src/lib/ProjectM36/DataTypes/SQL/Null.hs index fb09b8f7..d1014675 100644 --- a/src/lib/ProjectM36/DataTypes/SQL/Null.hs +++ b/src/lib/ProjectM36/DataTypes/SQL/Null.hs @@ -205,7 +205,10 @@ sqlMax [RelationAtom relIn] = case oneTuple relIn of Nothing -> pure $ nullAtom IntegerAtomType Nothing -- SQL max of empty table is NULL Just oneTup -> - pure $ relFold (\tupIn acc -> nullMax acc (newVal tupIn)) (newVal oneTup) relIn + if atomTypeForAtom (newVal oneTup) /= IntegerAtomType then + Left AtomFunctionTypeMismatchError + else + pure $ relFold (\tupIn acc -> nullMax acc (newVal tupIn)) (newVal oneTup) relIn where newVal tupIn = tupleAtoms tupIn V.! 0 nullMax acc nextVal = diff --git a/src/lib/ProjectM36/RelationalExpression.hs b/src/lib/ProjectM36/RelationalExpression.hs index 38a03139..fba9858f 100644 --- a/src/lib/ProjectM36/RelationalExpression.hs +++ b/src/lib/ProjectM36/RelationalExpression.hs @@ -880,7 +880,7 @@ evalGraphRefAtomExpr tupIn (FunctionAtomExpr funcName' arguments tid) = do lift $ except (atomTypeVerify expType actType)) zippedArgs evaldArgs <- mapM (evalGraphRefAtomExpr tupIn) arguments case evalAtomFunction func evaldArgs of - Left err -> traceShow ("evalGraphrefAtomExpr"::String, funcName', arguments) $ throwError (AtomFunctionUserError err) + Left err -> throwError (AtomFunctionUserError err) Right result -> do --validate that the result matches the expected type _ <- lift $ except (atomTypeVerify (last (funcType func)) (atomTypeForAtom result)) diff --git a/src/lib/ProjectM36/SQL/Convert.hs b/src/lib/ProjectM36/SQL/Convert.hs index 4f769b19..b34c9d10 100644 --- a/src/lib/ProjectM36/SQL/Convert.hs +++ b/src/lib/ProjectM36/SQL/Convert.hs @@ -687,7 +687,10 @@ convertProjection typeF selItems groupBys havingExpr = do task <- foldM (convertSelectItem typeF) emptyTask (zip [1::Int ..] selItems) -- traceShowM ("convertProjection task"::String, task) -- SQL supports only one grouping at a time, but multiple aggregations, so we create the group as attribute "_sql_aggregate" and the aggregations as fold projections on it - fGroup <- if not (null (nonAggregates groupInfo)) then + fGroup <- if not (null (nonAggregates groupInfo)) || + (null (nonAggregates groupInfo) && not (null (aggregates groupInfo))) + -- special case: SELECT max(status) FROM city- handle aggregations without GROUP BY + then pure $ Group (InvertedAttributeNames (S.fromList (map fst (nonAggregates groupInfo)))) "_sql_aggregate" else @@ -1384,7 +1387,7 @@ data GroupByItem = AggGroupByItem ProjectionScalarExpr GroupByExpr | data GroupByInfo = GroupByInfo { aggregates :: [ProjectionScalarExpr], -- ^ mentioned in group by clause and uses aggregation nonAggregates :: [(AttributeName, GroupByExpr)], -- ^ mentioned in group by clause by not aggregations - havingRestriction :: Maybe ProjectionScalarExpr + havingRestriction :: Maybe ProjectionScalarExpr } deriving (Show, Eq) diff --git a/test/SQL/InterpreterTest.hs b/test/SQL/InterpreterTest.hs index 916f21b0..ceb2aae0 100644 --- a/test/SQL/InterpreterTest.hs +++ b/test/SQL/InterpreterTest.hs @@ -162,8 +162,8 @@ testSelect = TestCase $ do "(relation{city Text, status SQLNullable Integer}{tuple{city \"London\", status SQLJust 20}, tuple{city \"Paris\", status SQLJust 30}, tuple{city \"Athens\", status SQLJust 30}})"), -- aggregate without grouping ("SELECT max(status) as status FROM s", - "(((true:{status:=s{status}}):{max:=max(@status)}){max})", - "(relation{status SQLNullable Integer}{tuple{status SQLJust 30}})") + "(((s group ({all but } as `_sql_aggregate`)):{status:=sql_max( (@`_sql_aggregate`){ status } )}){ status })", + "(relation{status SQLNullable Integer}{tuple{status SQLJust 30}})"), -- group by having -- limit -- case when