1{-# LANGUAGE ScopedTypeVariables #-}
2module ProjectM36.RelationalExpression where
3import ProjectM36.Relation
5import ProjectM36.TupleSet
8import ProjectM36.AtomType
9import ProjectM36.Attribute (emptyAttributes)
10import ProjectM36.ScriptSession
11import ProjectM36.DataTypes.Primitive
12import ProjectM36.AtomFunction
13import ProjectM36.DatabaseContextFunction
14import ProjectM36.Arbitrary
15import qualified ProjectM36.Attribute as A
16import qualified Data.Map as M
17import qualified Data.HashSet as HS
18import qualified Data.Set as S
19import Control.Monad.State hiding (join)
22import Data.Char (isUpper)
23import qualified Data.Text as T
24import qualified Data.Vector as V
25import qualified ProjectM36.TypeConstructorDef as TCD
26import Control.Monad.Trans.Except
27import Control.Monad.Trans.Reader
30#ifdef PM36_HASKELL_SCRIPTING
32import Control.Exception
37data DatabaseContextExprDetails = CountUpdatedTuples
39databaseContextExprDetailsFunc :: DatabaseContextExprDetails -> ResultAccumFunc
40databaseContextExprDetailsFunc CountUpdatedTuples _ relIn = Relation attrs newTups
42 attrs = A.attributesFromList [Attribute "count" IntAtomType]
43 existingTuple = fromMaybe (error "impossible counting error in singletonTuple") (singletonTuple relIn)
44 existingCount = case V.head (tupleAtoms existingTuple) of
46 _ -> error "impossible counting error in tupleAtoms"
47 newTups = case mkTupleSetFromList attrs [[IntAtom (existingCount + 1)]] of
48 Left err -> error ("impossible counting error in " ++ show err)
51-- | Used to start a fresh database state for a new database context expression.
52freshDatabaseState :: DatabaseContext -> DatabaseStateElems
53freshDatabaseState ctx = (ctx, M.empty, False) --future work: propagate return accumulator
55-- we need to pass around a higher level RelationTuple and Attributes in order to solve #52
56data RelationalExprStateElems = RelationalExprStateTupleElems DatabaseContext RelationTuple | -- used when fully evaluating a relexpr
57 RelationalExprStateAttrsElems DatabaseContext Attributes | --used when evaluating the type of a relexpr
58 RelationalExprStateElems DatabaseContext --used by default at the top level of evaluation
60instance Show RelationalExprStateElems where
61 show (RelationalExprStateTupleElems _ tup) = "RelationalExprStateTupleElems " ++ show tup
62 show (RelationalExprStateAttrsElems _ attrs) = "RelationalExprStateAttrsElems" ++ show attrs
63 show (RelationalExprStateElems _) = "RelationalExprStateElems"
65mkRelationalExprState :: DatabaseContext -> RelationalExprStateElems
66mkRelationalExprState = RelationalExprStateElems
68mergeTuplesIntoRelationalExprState :: RelationTuple -> RelationalExprStateElems -> RelationalExprStateElems
69mergeTuplesIntoRelationalExprState tupIn (RelationalExprStateElems ctx) = RelationalExprStateTupleElems ctx tupIn
70mergeTuplesIntoRelationalExprState _ st@(RelationalExprStateAttrsElems _ _) = st
71mergeTuplesIntoRelationalExprState tupIn (RelationalExprStateTupleElems ctx existingTuple) = let mergedTupMap = M.union (tupleToMap tupIn) (tupleToMap existingTuple) in
72 RelationalExprStateTupleElems ctx (mkRelationTupleFromMap mergedTupMap)
74mergeAttributesIntoRelationalExprState :: Attributes -> RelationalExprStateElems -> RelationalExprStateElems
75mergeAttributesIntoRelationalExprState attrs (RelationalExprStateElems ctx) = RelationalExprStateAttrsElems ctx attrs
76mergeAttributesIntoRelationalExprState _ st@(RelationalExprStateTupleElems _ _) = st
77mergeAttributesIntoRelationalExprState attrsIn (RelationalExprStateAttrsElems ctx attrs) = RelationalExprStateAttrsElems ctx (A.union attrsIn attrs)
79type ResultAccumName = StringType
81type ResultAccumFunc = (RelationTuple -> Relation -> Relation) -> Relation -> Relation
83data ResultAccum = ResultAccum { resultAccumFunc :: ResultAccumFunc,
84 resultAccumResult :: Relation
87type DatabaseStateElems = (DatabaseContext, M.Map ResultAccumName ResultAccum, DirtyFlag)
89type DatabaseState a = State DatabaseStateElems a
91getStateContext :: DatabaseState DatabaseContext
96putStateContext :: DatabaseContext -> DatabaseState ()
97putStateContext ctx = do
99 put (ctx, accum, True)
101type RelationalExprState a = Reader RelationalExprStateElems a
103stateElemsContext :: RelationalExprStateElems -> DatabaseContext
104stateElemsContext (RelationalExprStateTupleElems ctx _) = ctx
105stateElemsContext (RelationalExprStateElems ctx) = ctx
106stateElemsContext (RelationalExprStateAttrsElems ctx _) = ctx
108setStateElemsContext :: RelationalExprStateElems -> DatabaseContext -> RelationalExprStateElems
109setStateElemsContext (RelationalExprStateTupleElems _ tup) ctx = RelationalExprStateTupleElems ctx tup
110setStateElemsContext (RelationalExprStateElems _) ctx = RelationalExprStateElems ctx
111setStateElemsContext (RelationalExprStateAttrsElems _ attrs) ctx = RelationalExprStateAttrsElems ctx attrs
113--relvar state is needed in evaluation of relational expression but only as read-only in order to extract current relvar values
114evalRelationalExpr :: RelationalExpr -> RelationalExprState (Either RelationalError Relation)
115evalRelationalExpr (RelationVariable name _) = do
116 relvarTable <- fmap (relationVariables . stateElemsContext) ask
117 return $ case M.lookup name relvarTable of
118 Just res -> Right res
119 Nothing -> Left $ RelVarNotDefinedError name
121evalRelationalExpr (Project attrNames expr) = do
122 eAttrNameSet <- evalAttributeNames attrNames expr
124 Left err -> pure (Left err)
125 Right attrNameSet -> do
126 rel <- evalRelationalExpr expr
128 Right rel2 -> pure $ project attrNameSet rel2
129 Left err -> pure $ Left err
131evalRelationalExpr (Union exprA exprB) = do
132 relA <- evalRelationalExpr exprA
133 relB <- evalRelationalExpr exprB
135 Left err -> return $ Left err
136 Right relA2 -> case relB of
137 Left err -> return $ Left err
138 Right relB2 -> return $ union relA2 relB2
140evalRelationalExpr (Join exprA exprB) = do
141 relA <- evalRelationalExpr exprA
142 relB <- evalRelationalExpr exprB
144 Left err -> return $ Left err
145 Right relA2 -> case relB of
146 Left err -> return $ Left err
147 Right relB2 -> return $ join relA2 relB2
149evalRelationalExpr (Difference exprA exprB) = do
150 relA <- evalRelationalExpr exprA
151 relB <- evalRelationalExpr exprB
153 Left err -> return $ Left err
154 Right relA2 -> case relB of
155 Left err -> return $ Left err
156 Right relB2 -> return $ difference relA2 relB2
158evalRelationalExpr (MakeStaticRelation attributeSet tupleSet) =
159 case mkRelation attributeSet tupleSet of
160 Right rel -> return $ Right rel
161 Left err -> return $ Left err
163evalRelationalExpr (MakeRelationFromExprs mAttrExprs tupleExprs) = do
164 currentContext <- fmap stateElemsContext ask
165 let tConss = typeConstructorMapping currentContext
166 -- if the mAttrExprs is Nothing, then we should attempt to infer the tuple attributes from the first tuple itself- note that this is not always possible
168 mAttrs <- case mAttrExprs of
170 Just . A.attributesFromList <$> mapM (either throwE pure . evalAttrExpr tConss) (fromMaybe [] mAttrExprs)
171 Nothing -> pure Nothing
172 tuples <- mapM (liftE . evalTupleExpr mAttrs) tupleExprs
173 let attrs = fromMaybe firstTupleAttrs mAttrs
174 firstTupleAttrs = if null tuples then A.emptyAttributes else tupleAttributes (head tuples)
175 either throwE pure (mkRelation attrs (RelationTupleSet tuples))
177evalRelationalExpr (ExistingRelation rel) = pure (Right rel)
179evalRelationalExpr (Rename oldAttrName newAttrName relExpr) = do
180 evald <- evalRelationalExpr relExpr
182 Right rel -> return $ rename oldAttrName newAttrName rel
183 Left err -> return $ Left err
185evalRelationalExpr (Group oldAttrNames newAttrName relExpr) = do
186 eOldAttrNameSet <- evalAttributeNames oldAttrNames relExpr
187 case eOldAttrNameSet of
188 Left err -> pure (Left err)
189 Right oldAttrNameSet -> do
190 evald <- evalRelationalExpr relExpr
192 Right rel -> return $ group oldAttrNameSet newAttrName rel
193 Left err -> return $ Left err
195evalRelationalExpr (Ungroup attrName relExpr) = do
196 evald <- evalRelationalExpr relExpr
198 Right rel -> return $ ungroup attrName rel
199 Left err -> return $ Left err
201evalRelationalExpr (Restrict predicateExpr relExpr) = do
202 evald <- evalRelationalExpr relExpr
204 Left err -> return $ Left err
206 eFilterFunc <- predicateRestrictionFilter (attributes rel) predicateExpr
208 Left err -> return $ Left err
210 pure (restrict filterfunc rel)
212evalRelationalExpr (Equals relExprA relExprB) = do
213 evaldA <- evalRelationalExpr relExprA
214 evaldB <- evalRelationalExpr relExprB
216 Left err -> return $ Left err
217 Right relA -> case evaldB of
218 Left err -> return $ Left err
219 Right relB -> return $ Right $ if relA == relB then relationTrue else relationFalse
221evalRelationalExpr (With views mainExpr) = do
224 let addScopedView ctx (vname,vexpr) = if vname `M.member` relationVariables ctx then
225 Left (RelVarAlreadyDefinedError vname)
227 case runState (evalDatabaseContextExpr (Assign vname vexpr)) (freshDatabaseState ctx) of
228 (Left err,_) -> Left err
229 (Right (), (ctx',_,_)) -> Right ctx'
231 case foldM addScopedView (stateElemsContext rstate) views of
232 Left err -> return $ Left err
234 let evalMainExpr expr = runReader (evalRelationalExpr expr) (RelationalExprStateElems ctx'')
235 case evalMainExpr mainExpr of
236 Left err -> return $ Left err
237 Right rel -> return $ Right rel
239--warning: copy-pasta from above- refactor
240evalRelationalExpr (NotEquals relExprA relExprB) = do
241 evaldA <- evalRelationalExpr relExprA
242 evaldB <- evalRelationalExpr relExprB
244 Left err -> return $ Left err
245 Right relA -> case evaldB of
246 Left err -> return $ Left err
247 Right relB -> return $ Right $ if relA /= relB then relationTrue else relationFalse
249-- extending a relation adds a single attribute with the results of the per-tuple expression evaluated
250evalRelationalExpr (Extend tupleExpression relExpr) = do
252 let evald = runReader (evalRelationalExpr relExpr) rstate
254 Left err -> pure (Left err)
256 tupProc <- extendTupleExpressionProcessor rel tupleExpression
258 Left err -> pure (Left err)
259 Right (newAttrs, tupProc') -> pure $ relMogrify tupProc' newAttrs rel
261--helper function to process relation variable creation/assignment
262setRelVar :: RelVarName -> Relation -> DatabaseState (Either RelationalError ())
263setRelVar relVarName rel = do
264 currentContext <- getStateContext
265 let newRelVars = M.insert relVarName rel $ relationVariables currentContext
266 potentialContext = currentContext { relationVariables = newRelVars }
268 case checkConstraints potentialContext of
269 Left err -> pure (Left err)
271 putStateContext potentialContext
274-- it is not an error to delete a relvar which does not exist, just like it is not an error to insert a pre-existing tuple into a relation
275deleteRelVar :: RelVarName -> DatabaseState (Either RelationalError ())
276deleteRelVar relVarName = do
277 currContext <- getStateContext
278 let relVars = relationVariables currContext
279 if M.notMember relVarName relVars then
282 let newRelVars = M.delete relVarName relVars
283 newContext = currContext { relationVariables = newRelVars }
284 putStateContext newContext
287evalDatabaseContextExpr :: DatabaseContextExpr -> DatabaseState (Either RelationalError ())
288evalDatabaseContextExpr NoOperation = pure (Right ())
290evalDatabaseContextExpr (Define relVarName attrExprs) = do
291 relvars <- fmap relationVariables getStateContext
292 tConss <- fmap typeConstructorMapping getStateContext
293 let eAttrs = map (evalAttrExpr tConss) attrExprs
294 attrErrs = lefts eAttrs
295 attrsList = rights eAttrs
296 if not (null attrErrs) then
297 pure (Left (someErrors attrErrs))
299 let atomTypeErrs = lefts $ map ((`validateAtomType` tConss) . A.atomType) attrsList
300 if not (null atomTypeErrs) then
301 pure (Left (someErrors atomTypeErrs))
303 case M.member relVarName relvars of
304 True -> pure (Left (RelVarAlreadyDefinedError relVarName))
305 False -> setRelVar relVarName emptyRelation >> pure (Right ())
307 attrs = A.attributesFromList attrsList
308 emptyRelation = Relation attrs emptyTupleSet
310evalDatabaseContextExpr (Undefine relVarName) = deleteRelVar relVarName
312evalDatabaseContextExpr (Assign relVarName expr) = do
313 -- in the future, it would be nice to get types from the RelationalExpr instead of needing to evaluate it
314 context <- getStateContext
315 let existingRelVar = M.lookup relVarName relVarTable
316 relVarTable = relationVariables context
317 value = runReader (evalRelationalExpr expr) (RelationalExprStateElems context)
319 Left err -> pure (Left err)
320 Right rel -> case existingRelVar of
321 Nothing -> setRelVar relVarName rel
322 Just existingRel -> let expectedAttributes = attributes existingRel
323 foundAttributes = attributes rel in
324 if A.attributesEqual expectedAttributes foundAttributes then
325 setRelVar relVarName rel
327 pure (Left (RelationTypeMismatchError expectedAttributes foundAttributes))
329evalDatabaseContextExpr (Insert relVarName relExpr) = do
330 context <- getStateContext
331 let unionexp = Union relExpr rv
332 rv = RelationVariable relVarName ()
333 unioned = runReader (evalRelationalExpr unionexp) (RelationalExprStateElems context)
334 origRel = runReader (evalRelationalExpr rv) (RelationalExprStateElems context)
336 Left err -> pure (Left err)
337 Right unioned' -> case origRel of
338 Left err -> pure (Left err)
339 Right origRel' -> if cardinality unioned' == cardinality origRel' then --no tuples actually inserted
342 evalDatabaseContextExpr $ Assign relVarName (ExistingRelation unioned')
344evalDatabaseContextExpr (Delete relVarName predicate) = do
345 context <- getStateContext
346 let rv = RelationVariable relVarName ()
347 let updatedRel = runReader (evalRelationalExpr (Restrict (NotPredicate predicate) rv)) (RelationalExprStateElems context)
348 origRel = runReader (evalRelationalExpr rv) (RelationalExprStateElems context)
350 Left err -> pure (Left err)
351 Right updatedRel' -> case origRel of
352 Left err -> pure (Left err)
353 Right origRel' -> if cardinality origRel' == cardinality updatedRel' then
356 setRelVar relVarName updatedRel'
358--union of restricted+updated portion and the unrestricted+unupdated portion
359evalDatabaseContextExpr (Update relVarName atomExprMap restrictionPredicateExpr) = do
360 context <- getStateContext
361 let relVarTable = relationVariables context
362 case M.lookup relVarName relVarTable of
363 Nothing -> pure (Left (RelVarNotDefinedError relVarName))
365 case runReader (predicateRestrictionFilter (attributes rel) restrictionPredicateExpr) (RelationalExprStateElems context) of
366 Left err -> pure (Left err)
367 Right predicateFunc -> do
369 restrictedPortion <- restrict predicateFunc rel
370 if cardinality restrictedPortion == Finite 0 then
373 unrestrictedPortion <- restrict (predicateFunc >=> (pure . not)) rel
374 updatedPortion <- relMap (updateTupleWithAtomExprs atomExprMap context) restrictedPortion
375 updatedRel <- updatedPortion `union` unrestrictedPortion
376 pure (Just updatedRel)
378 Left err -> pure (Left err)
379 Right Nothing -> pure (Right ())
380 Right (Just updatedRel) -> setRelVar relVarName updatedRel
382evalDatabaseContextExpr (AddInclusionDependency newDepName newDep) = do
383 currContext <- getStateContext
384 let currDeps = inclusionDependencies currContext
385 newDeps = M.insert newDepName newDep currDeps
386 if M.member newDepName currDeps then
387 pure (Left (InclusionDependencyNameInUseError newDepName))
389 let potentialContext = currContext { inclusionDependencies = newDeps }
390 case checkConstraints potentialContext of
391 Left err -> pure (Left err)
393 putStateContext potentialContext
396evalDatabaseContextExpr (RemoveInclusionDependency depName) = do
397 currContext <- getStateContext
398 let currDeps = inclusionDependencies currContext
399 newDeps = M.delete depName currDeps
400 if M.notMember depName currDeps then
401 pure (Left (InclusionDependencyNameNotInUseError depName))
403 putStateContext $ currContext {inclusionDependencies = newDeps }
406-- | Add a notification which will send the resultExpr when triggerExpr changes between commits.
407evalDatabaseContextExpr (AddNotification notName triggerExpr resultOldExpr resultNewExpr) = do
408 currentContext <- getStateContext
409 let nots = notifications currentContext
410 if M.member notName nots then
411 pure (Left (NotificationNameInUseError notName))
413 let newNotifications = M.insert notName newNotification nots
414 newNotification = Notification { changeExpr = triggerExpr,
415 reportOldExpr = resultOldExpr,
416 reportNewExpr = resultNewExpr}
417 putStateContext $ currentContext { notifications = newNotifications }
420evalDatabaseContextExpr (RemoveNotification notName) = do
421 currentContext <- getStateContext
422 let nots = notifications currentContext
423 if M.notMember notName nots then
424 pure (Left (NotificationNameNotInUseError notName))
426 let newNotifications = M.delete notName nots
427 putStateContext $ currentContext { notifications = newNotifications }
430-- | Adds type and data constructors to the database context.
431-- validate that the type *and* constructor names are unique! not yet implemented!
432evalDatabaseContextExpr (AddTypeConstructor tConsDef dConsDefList) = do
433 currentContext <- getStateContext
434 let oldTypes = typeConstructorMapping currentContext
435 tConsName = TCD.name tConsDef
436 -- validate that the constructor's types exist
437 case validateTypeConstructorDef tConsDef dConsDefList oldTypes of
438 Left err -> pure (Left err)
439 Right _ | T.null tConsName || not (isUpper (T.head tConsName)) -> pure (Left (InvalidAtomTypeName tConsName))
440 | isJust (findTypeConstructor tConsName oldTypes) -> pure (Left (AtomTypeNameInUseError tConsName))
442 let newTypes = oldTypes ++ [(tConsDef, dConsDefList)]
443 putStateContext $ currentContext { typeConstructorMapping = newTypes }
446-- | Removing the atom constructor prevents new atoms of the type from being created. Existing atoms of the type remain. Thus, the atomTypes list in the DatabaseContext need not be all-inclusive.
447evalDatabaseContextExpr (RemoveTypeConstructor tConsName) = do
448 currentContext <- getStateContext
449 let oldTypes = typeConstructorMapping currentContext
450 if isNothing (findTypeConstructor tConsName oldTypes) then
451 pure (Left (AtomTypeNameNotInUseError tConsName))
453 let newTypes = filter (\(tCons, _) -> TCD.name tCons /= tConsName) oldTypes
454 putStateContext $ currentContext { typeConstructorMapping = newTypes }
457evalDatabaseContextExpr (MultipleExpr exprs) = do
458 --the multiple expressions must pass the same context around- not the old unmodified context
459 evald <- forM exprs evalDatabaseContextExpr
460 --some lifting magic needed here
462 [] -> pure (Right ())
463 errs -> pure (Left (someErrors errs))
465evalDatabaseContextExpr (RemoveAtomFunction funcName) = do
466 currentContext <- getStateContext
467 let atomFuncs = atomFunctions currentContext
468 case atomFunctionForName funcName atomFuncs of
469 Left err -> pure (Left err)
470 Right realFunc -> if isScriptedAtomFunction realFunc then do
471 let updatedFuncs = HS.delete realFunc atomFuncs
472 putStateContext (currentContext {atomFunctions = updatedFuncs })
475 pure (Left (PrecompiledFunctionRemoveError funcName))
477evalDatabaseContextExpr (RemoveDatabaseContextFunction funcName) = do
478 context <- getStateContext
479 let dbcFuncs = dbcFunctions context
480 case databaseContextFunctionForName funcName dbcFuncs of
481 Left err -> pure (Left err)
482 Right realFunc -> if isScriptedDatabaseContextFunction realFunc then do
483 let updatedFuncs = HS.delete realFunc dbcFuncs
484 putStateContext (context { dbcFunctions = updatedFuncs })
487 pure (Left (PrecompiledFunctionRemoveError funcName))
489evalDatabaseContextExpr (ExecuteDatabaseContextFunction funcName atomArgExprs) = do
490 context <- getStateContext
491 --resolve atom arguments
492 let relExprState = mkRelationalExprState context
493 eAtomTypes = map (\atomExpr -> runReader (typeFromAtomExpr emptyAttributes atomExpr) relExprState) atomArgExprs
494 eFunc = databaseContextFunctionForName funcName (dbcFunctions context)
496 Left err -> pure (Left err)
498 let expectedArgCount = length (dbcFuncType func)
499 actualArgCount = length atomArgExprs
500 if expectedArgCount /= actualArgCount then
501 pure (Left (FunctionArgumentCountMismatchError expectedArgCount actualArgCount))
503 --check that the atom types are valid
504 case lefts eAtomTypes of
505 _:_ -> pure (Left (someErrors (lefts eAtomTypes)))
507 let atomTypes = rights eAtomTypes
508 let mValidTypes = zipWith
510 -> case atomTypeVerify expType actType of
515 typeErrors = catMaybes mValidTypes
516 eAtomArgs = map (\arg -> runReader (evalAtomExpr emptyTuple arg) relExprState) atomArgExprs
517 if length (lefts eAtomArgs) > 1 then
518 pure (Left (someErrors (lefts eAtomArgs)))
519 else if not (null typeErrors) then
520 pure (Left (someErrors typeErrors))
522 case evalDatabaseContextFunction func (rights eAtomArgs) context of
523 Left err -> pure (Left err)
524 Right newContext -> putStateContext newContext >> pure (Right ())
526evalDatabaseContextIOExpr :: Maybe ScriptSession -> DatabaseContext -> DatabaseContextIOExpr -> IO (Either RelationalError DatabaseContext)
527#if !defined(PM36_HASKELL_SCRIPTING)
528evalDatabaseContextIOExpr _ _ AddAtomFunction{} = pure (Left (ScriptError ScriptCompilationDisabledError))
529evalDatabaseContextIOExpr _ _ AddDatabaseContextFunction{} = pure (Left (ScriptError ScriptCompilationDisabledError))
530evalDatabaseContextIOExpr _ _ LoadAtomFunctions{} = pure (Left (ScriptError ScriptCompilationDisabledError))
531evalDatabaseContextIOExpr _ _ LoadDatabaseContextFunctions{} = pure (Left (ScriptError ScriptCompilationDisabledError))
533evalDatabaseContextIOExpr mScriptSession currentContext (AddAtomFunction funcName funcType script) =
534 case mScriptSession of
535 Nothing -> pure (Left (ScriptError ScriptCompilationDisabledError))
536 Just scriptSession -> do
537 res <- try $ runGhc (Just libdir) $ do
538 setSession (hscEnv scriptSession)
539 let atomFuncs = atomFunctions currentContext
540 case extractAtomFunctionType funcType of
541 Left err -> pure (Left err)
542 Right adjustedAtomTypeCons -> do
543 --compile the function
544 eCompiledFunc <- compileScript (atomFunctionBodyType scriptSession) script
545 pure $ case eCompiledFunc of
546 Left err -> Left (ScriptError err)
547 Right compiledFunc -> do
548 funcAtomType <- mapM (\funcTypeArg -> atomTypeForTypeConstructorValidate False funcTypeArg (typeConstructorMapping currentContext) M.empty) adjustedAtomTypeCons
549 let updatedFuncs = HS.insert newAtomFunc atomFuncs
550 newContext = currentContext { atomFunctions = updatedFuncs }
551 newAtomFunc = AtomFunction { atomFuncName = funcName,
552 atomFuncType = funcAtomType,
553 atomFuncBody = AtomFunctionBody (Just script) compiledFunc }
554 -- check if the name is already in use
555 if HS.member funcName (HS.map atomFuncName atomFuncs) then
556 Left (FunctionNameInUseError funcName)
560 Left (exc :: SomeException) -> pure $ Left (ScriptError (OtherScriptCompilationError (show exc)))
561 Right eContext -> case eContext of
562 Left err -> pure (Left err)
563 Right context' -> pure (Right context')
565evalDatabaseContextIOExpr mScriptSession currentContext (AddDatabaseContextFunction funcName funcType script) =
566 case mScriptSession of
567 Nothing -> pure (Left (ScriptError ScriptCompilationDisabledError))
568 Just scriptSession -> do
569 --validate that the function signature is of the form x -> y -> ... -> DatabaseContext -> DatabaseContext
570 let last2Args = reverse (take 2 (reverse funcType))
571 atomArgs = take (length funcType - 2) funcType
572 dbContextTypeCons = ADTypeConstructor "Either" [ADTypeConstructor "DatabaseContextFunctionError" [], ADTypeConstructor "DatabaseContext" []]
573 expectedType = "DatabaseContext -> Either DatabaseContextFunctionError DatabaseContext"
574 actualType = show funcType
575 if last2Args /= [ADTypeConstructor "DatabaseContext" [], dbContextTypeCons] then
576 pure (Left (ScriptError (TypeCheckCompilationError expectedType actualType)))
578 res <- try $ runGhc (Just libdir) $ do
579 setSession (hscEnv scriptSession)
580 eCompiledFunc <- compileScript (dbcFunctionBodyType scriptSession) script
581 pure $ case eCompiledFunc of
582 Left err -> Left (ScriptError err)
583 Right compiledFunc -> do
584 --if we are here, we have validated that the written function type is X -> DatabaseContext -> DatabaseContext, so we need to munge the first elements into an array
585 funcAtomType <- mapM (\funcTypeArg -> atomTypeForTypeConstructor funcTypeArg (typeConstructorMapping currentContext) M.empty) atomArgs
586 let updatedDBCFuncs = HS.insert newDBCFunc (dbcFunctions currentContext)
587 newContext = currentContext { dbcFunctions = updatedDBCFuncs }
588 dbcFuncs = dbcFunctions currentContext
589 newDBCFunc = DatabaseContextFunction {
590 dbcFuncName = funcName,
591 dbcFuncType = funcAtomType,
592 dbcFuncBody = DatabaseContextFunctionBody (Just script) compiledFunc
594 -- check if the name is already in use
595 if HS.member funcName (HS.map dbcFuncName dbcFuncs) then
596 Left (FunctionNameInUseError funcName)
600 Left (exc :: SomeException) -> pure $ Left (ScriptError (OtherScriptCompilationError (show exc)))
601 Right eContext -> case eContext of
602 Left err -> pure (Left err)
603 Right context' -> pure (Right context')
604evalDatabaseContextIOExpr _ currentContext (LoadAtomFunctions modName funcName modPath) = do
605 eLoadFunc <- loadAtomFunctions (T.unpack modName) (T.unpack funcName) modPath
607 Left LoadSymbolError -> pure (Left LoadFunctionError)
608 Right atomFunctionListFunc -> let newContext = currentContext { atomFunctions = mergedFuncs }
609 mergedFuncs = HS.union (atomFunctions currentContext) (HS.fromList atomFunctionListFunc)
610 in pure (Right newContext)
611evalDatabaseContextIOExpr _ currentContext (LoadDatabaseContextFunctions modName funcName modPath) = do
612 eLoadFunc <- loadDatabaseContextFunctions (T.unpack modName) (T.unpack funcName) modPath
614 Left LoadSymbolError -> pure (Left LoadFunctionError)
615 Right dbcListFunc -> let newContext = currentContext { dbcFunctions = mergedFuncs }
616 mergedFuncs = HS.union (dbcFunctions currentContext) (HS.fromList dbcListFunc)
617 in pure (Right newContext)
620evalDatabaseContextIOExpr _ currentContext (CreateArbitraryRelation relVarName attrExprs range) =
622 case runState ( evalDatabaseContextExpr (Define relVarName attrExprs)) (freshDatabaseState currentContext) of
623 (Left err,_) -> pure (Left err)
624 (Right (), elems@(ctx,_,_)) -> do
626 let existingRelVar = M.lookup relVarName relVarTable
627 relVarTable = relationVariables ctx
628 case existingRelVar of
629 Nothing -> pure $ Left (RelVarNotDefinedError relVarName)
630 Just existingRel -> do
631 let expectedAttributes = attributes existingRel
632 let tcMap = typeConstructorMapping ctx
633 eitherRel <- generate $ runReaderT (arbitraryRelation expectedAttributes range) tcMap
635 Left err -> pure $ Left err
637 case runState (setRelVar relVarName rel) elems of
638 (Left err,_) -> pure (Left err)
639 (Right (), (ctx',_,_)) -> pure $ Right ctx'
641updateTupleWithAtomExprs :: M.Map AttributeName AtomExpr -> DatabaseContext -> RelationTuple -> Either RelationalError RelationTuple
642updateTupleWithAtomExprs exprMap context tupIn = do
643 --resolve all atom exprs
644 atomsAssoc <- mapM (\(attrName, atomExpr) -> do
645 atom <- runReader (evalAtomExpr tupIn atomExpr) (RelationalExprStateElems context)
646 pure (attrName, atom)
648 pure (updateTupleWithAtoms (M.fromList atomsAssoc) tupIn)
650--run verification on all constraints
651checkConstraints :: DatabaseContext -> Either RelationalError ()
652checkConstraints context =
653 mapM_ (uncurry checkIncDep) (M.toList deps)
655 deps = inclusionDependencies context
656 eval expr = evalReader (evalRelationalExpr expr)
657 evalReader f = runReader f (RelationalExprStateElems context)
658 checkIncDep depName (InclusionDependency subsetExpr supersetExpr) = do
659 --if both expressions are of a single-attribute (such as with a simple foreign key), the names of the attributes are irrelevant (they need not match) because the expression is unambiguous, but special-casing this to rename the attribute automatically would not be orthogonal behavior and probably cause confusion. Instead, special case the error to make it clear.
660 typeSub <- evalReader (typeForRelationalExpr subsetExpr)
661 typeSuper <- evalReader (typeForRelationalExpr supersetExpr)
662 when (typeSub /= typeSuper) (Left (RelationTypeMismatchError (attributes typeSub) (attributes typeSuper)))
663 let checkExpr = Equals supersetExpr (Union subsetExpr supersetExpr)
664 case eval checkExpr of
666 Right resultRel -> if resultRel == relationTrue then
669 Left (InclusionDependencyCheckError depName)
671-- the type of a relational expression is equal to the relation attribute set returned from executing the relational expression; therefore, the type can be cheaply derived by evaluating a relational expression and ignoring and tuple processing
672-- furthermore, the type of a relational expression is the resultant header of the evaluated empty-tupled relation
674typeForRelationalExpr :: RelationalExpr -> RelationalExprState (Either RelationalError Relation)
675typeForRelationalExpr expr = do
677 let context = stateElemsContext rstate
678 --replace the relationVariables context element with a cloned set of relation devoid of tuples
679 let context' = contextWithEmptyTupleSets context
680 rstate' = setStateElemsContext rstate context'
681 --evalRelationalExpr could still return an existing relation with tuples, so strip them
682 pure $ case runReader (evalRelationalExpr expr) rstate' of
684 Right typeRel -> Right (relationWithEmptyTupleSet typeRel)
686--returns a database context with all tuples removed
687--this is useful for type checking and optimization
688contextWithEmptyTupleSets :: DatabaseContext -> DatabaseContext
689contextWithEmptyTupleSets contextIn = contextIn { relationVariables = relVars }
691 relVars = M.map (\rel -> Relation (attributes rel) emptyTupleSet) (relationVariables contextIn)
693liftE :: (Monad m) => m (Either a b) -> ExceptT a m b
697 Left err -> throwE err
698 Right val -> pure val
700{- used for restrictions- take the restrictionpredicate and return the corresponding filter function -}
701predicateRestrictionFilter :: Attributes -> RestrictionPredicateExpr -> RelationalExprState (Either RelationalError RestrictionFilter)
702predicateRestrictionFilter attrs (AndPredicate expr1 expr2) =
704 expr1v <- liftE (predicateRestrictionFilter attrs expr1)
705 expr2v <- liftE (predicateRestrictionFilter attrs expr2)
711predicateRestrictionFilter attrs (OrPredicate expr1 expr2) =
713 expr1v <- liftE (predicateRestrictionFilter attrs expr1)
714 expr2v <- liftE (predicateRestrictionFilter attrs expr2)
720predicateRestrictionFilter _ TruePredicate = pure (Right (\_ -> pure True))
722predicateRestrictionFilter attrs (NotPredicate expr) =
724 exprv <- liftE (predicateRestrictionFilter attrs expr)
725 pure (fmap not . exprv)
727--optimization opportunity: if the subexpression does not reference attributes in the top-level expression, then it need only be evaluated once, statically, outside the tuple filter- see historical implementation here
728predicateRestrictionFilter _ (RelationalExprPredicate relExpr) = do
730 pure (Right (\tup -> case runReader (evalRelationalExpr relExpr) (mergeTuplesIntoRelationalExprState tup rstate) of
732 Right rel -> if arity rel /= 0 then
733 Left (PredicateExpressionError "Relational restriction filter must evaluate to 'true' or 'false'")
735 pure (rel == relationTrue)))
737predicateRestrictionFilter attrs (AttributeEqualityPredicate attrName atomExpr) = do
739 let (attrs', ctxtup') = case rstate of
740 RelationalExprStateElems _ -> (attrs, emptyTuple)
741 RelationalExprStateAttrsElems _ _ -> (attrs, emptyTuple)
742 RelationalExprStateTupleElems _ ctxtup -> (A.union attrs (tupleAttributes ctxtup), ctxtup)
744 atomExprType <- liftE (typeFromAtomExpr attrs' atomExpr)
745 attr <- either throwE pure $ case A.attributeForName attrName attrs of
746 Right attr -> Right attr
747 Left (NoSuchAttributeNamesError _) -> case A.attributeForName attrName (tupleAttributes ctxtup') of
748 Right ctxattr -> Right ctxattr
749 Left err2@(NoSuchAttributeNamesError _) -> Left err2
752 if atomExprType /= A.atomType attr then
753 throwE (TupleAttributeTypeMismatchError (A.attributesFromList [attr]))
755 pure $ \tupleIn -> let evalAndCmp atomIn = case atomEvald of
756 Right atomCmp -> atomCmp == atomIn
758 atomEvald = runReader (evalAtomExpr tupleIn atomExpr) rstate
760 pure $ case atomForAttributeName attrName tupleIn of
761 Left (NoSuchAttributeNamesError _) -> case atomForAttributeName attrName ctxtup' of
763 Right ctxatom -> evalAndCmp ctxatom
765 Right atomIn -> evalAndCmp atomIn
766-- in the future, it would be useful to do typechecking on the attribute and atom expr filters in advance
767predicateRestrictionFilter attrs (AtomExprPredicate atomExpr) = do
768 --merge attrs into the state attributes
771 aType <- liftE (typeFromAtomExpr attrs atomExpr)
772 if aType /= BoolAtomType then
773 throwE (AtomTypeMismatchError aType BoolAtomType)
776 case runReader (evalAtomExpr tupleIn atomExpr) rstate of
778 Right boolAtomValue -> pure (boolAtomValue == BoolAtom True))
780tupleExprCheckNewAttrName :: AttributeName -> Relation -> Either RelationalError Relation
781tupleExprCheckNewAttrName attrName rel = if isRight (attributeForName attrName rel) then
782 Left (AttributeNameInUseError attrName)
786extendTupleExpressionProcessor :: Relation -> ExtendTupleExpr -> RelationalExprState (Either RelationalError (Attributes, RelationTuple -> Either RelationalError RelationTuple))
787extendTupleExpressionProcessor relIn (AttributeExtendTupleExpr newAttrName atomExpr) = do
789 -- check that the attribute name is not in use
790 case tupleExprCheckNewAttrName newAttrName relIn of
791 Left err -> pure (Left err)
792 Right _ -> runExceptT $ do
793 atomExprType <- liftE (typeFromAtomExpr (attributes relIn) atomExpr)
794 atomExprType' <- liftE (verifyAtomExprTypes relIn atomExpr atomExprType)
795 let newAttrs = A.attributesFromList [Attribute newAttrName atomExprType']
796 newAndOldAttrs = A.addAttributes (attributes relIn) newAttrs
797 pure (newAndOldAttrs, \tup -> let substate = mergeTuplesIntoRelationalExprState tup rstate in case runReader (evalAtomExpr tup atomExpr) substate of
799 Right atom -> Right (tupleAtomExtend newAttrName atom tup)
802evalAtomExpr :: RelationTuple -> AtomExpr -> RelationalExprState (Either RelationalError Atom)
803evalAtomExpr tupIn (AttributeAtomExpr attrName) = case atomForAttributeName attrName tupIn of
804 Right atom -> pure (Right atom)
805 err@(Left (NoSuchAttributeNamesError _)) -> do
808 RelationalExprStateElems _ -> pure err
809 RelationalExprStateAttrsElems _ _ -> pure err
810 RelationalExprStateTupleElems _ ctxtup -> pure (atomForAttributeName attrName ctxtup)
811 Left err -> pure (Left err)
812evalAtomExpr _ (NakedAtomExpr atom) = pure (Right atom)
813evalAtomExpr tupIn (FunctionAtomExpr funcName arguments ()) = do
814 argTypes <- mapM (typeFromAtomExpr (tupleAttributes tupIn)) arguments
815 context <- fmap stateElemsContext ask
817 let functions = atomFunctions context
818 func <- either throwE pure (atomFunctionForName funcName functions)
819 let expectedArgCount = length (atomFuncType func) - 1
820 actualArgCount = length argTypes
821 safeInit [] = [] -- different behavior from normal init
822 safeInit xs = init xs
823 if expectedArgCount /= actualArgCount then
824 throwE (FunctionArgumentCountMismatchError expectedArgCount actualArgCount)
826 let zippedArgs = zip (safeInit (atomFuncType func)) argTypes
827 mapM_ (\(expType, eActType) -> do
828 actType <- either throwE pure eActType
829 either throwE pure (atomTypeVerify expType actType)) zippedArgs
830 evaldArgs <- mapM (liftE . evalAtomExpr tupIn) arguments
831 case evalAtomFunction func evaldArgs of
832 Left err -> throwE (AtomFunctionUserError err)
834 --validate that the result matches the expected type
835 _ <- either throwE pure (atomTypeVerify (last (atomFuncType func)) (atomTypeForAtom result))
837evalAtomExpr tupIn (RelationAtomExpr relExpr) = do
838 --merge existing state tuple context into new state tuple context to support an arbitrary number of levels, but new attributes trounce old attributes
841 let newState = mergeTuplesIntoRelationalExprState tupIn rstate
842 relAtom <- either throwE pure (runReader (evalRelationalExpr relExpr) newState)
843 pure (RelationAtom relAtom)
844evalAtomExpr tupIn cons@(ConstructedAtomExpr dConsName dConsArgs ()) = runExceptT $ do
846 let newState = mergeTuplesIntoRelationalExprState tupIn rstate
847 aType <- either throwE pure (runReader (typeFromAtomExpr (tupleAttributes tupIn) cons) newState)
848 argAtoms <- mapM (\arg -> either throwE pure (runReader (evalAtomExpr tupIn arg) newState)) dConsArgs
849 pure (ConstructedAtom dConsName aType argAtoms)
851typeFromAtomExpr :: Attributes -> AtomExpr -> RelationalExprState (Either RelationalError AtomType)
852typeFromAtomExpr attrs (AttributeAtomExpr attrName) = do
854 case A.atomTypeForAttributeName attrName attrs of
855 Right aType -> pure (Right aType)
856 Left err@(NoSuchAttributeNamesError _) -> case rstate of
857 RelationalExprStateAttrsElems _ attrs' -> case A.attributeForName attrName attrs' of
858 Left err' -> pure (Left err')
859 Right attr -> pure (Right (A.atomType attr))
860 RelationalExprStateElems _ -> pure (Left err)
861 RelationalExprStateTupleElems _ tup -> case atomForAttributeName attrName tup of
862 Left err' -> pure (Left err')
863 Right atom -> pure (Right (atomTypeForAtom atom))
864 Left err -> pure (Left err)
865typeFromAtomExpr _ (NakedAtomExpr atom) = pure (Right (atomTypeForAtom atom))
866typeFromAtomExpr attrs (FunctionAtomExpr funcName atomArgs _) = do
867 context <- fmap stateElemsContext ask
868 let funcs = atomFunctions context
869 case atomFunctionForName funcName funcs of
870 Left err -> pure (Left err)
872 let funcRetType = last (atomFuncType func)
873 funcArgTypes = init (atomFuncType func)
874 eArgTypes <- mapM (typeFromAtomExpr attrs) atomArgs
875 case lefts eArgTypes of
876 errs@(_:_) -> pure (Left (someErrors errs))
878 let eTvMap = resolveTypeVariables funcArgTypes argTypes
879 argTypes = rights eArgTypes
881 Left err -> pure (Left err)
882 Right tvMap -> pure (resolveFunctionReturnValue funcName tvMap funcRetType)
883typeFromAtomExpr attrs (RelationAtomExpr relExpr) = runExceptT $ do
885 relType <- either throwE pure (runReader (typeForRelationalExpr relExpr) (mergeAttributesIntoRelationalExprState attrs rstate))
886 pure (RelationAtomType (attributes relType))
887-- grab the type of the data constructor, then validate that the args match the expected types
888typeFromAtomExpr attrs (ConstructedAtomExpr dConsName dConsArgs _) =
890 argsTypes <- mapM (liftE . typeFromAtomExpr attrs) dConsArgs
891 context <- fmap stateElemsContext (lift ask)
892 either throwE pure (atomTypeForDataConstructor (typeConstructorMapping context) dConsName argsTypes)
894-- | Validate that the type of the AtomExpr matches the expected type.
895verifyAtomExprTypes :: Relation -> AtomExpr -> AtomType -> RelationalExprState (Either RelationalError AtomType)
896verifyAtomExprTypes relIn (AttributeAtomExpr attrName) expectedType = runExceptT $ do
898 case A.atomTypeForAttributeName attrName (attributes relIn) of
899 Right aType -> either throwE pure (atomTypeVerify expectedType aType)
900 (Left err@(NoSuchAttributeNamesError _)) -> case rstate of
901 RelationalExprStateTupleElems _ _ -> throwE err
902 RelationalExprStateElems _ -> throwE err
903 RelationalExprStateAttrsElems _ attrs -> case A.attributeForName attrName attrs of
904 Left err' -> throwE err'
905 Right attrType -> either throwE pure (atomTypeVerify expectedType (A.atomType attrType))
906 Left err -> throwE err
907verifyAtomExprTypes _ (NakedAtomExpr atom) expectedType = pure (atomTypeVerify expectedType (atomTypeForAtom atom))
908verifyAtomExprTypes relIn (FunctionAtomExpr funcName funcArgExprs _) expectedType = do
910 let functions = atomFunctions context
911 context = stateElemsContext rstate
913 func <- either throwE pure (atomFunctionForName funcName functions)
914 let expectedArgTypes = atomFuncType func
915 funcArgTypes <- mapM (\(atomExpr,expectedType2,argCount) -> case runReader (verifyAtomExprTypes relIn atomExpr expectedType2) rstate of
916 Left (AtomTypeMismatchError expSubType actSubType) -> throwE (AtomFunctionTypeError funcName argCount expSubType actSubType)
917 Left err -> throwE err
919 ) $ zip3 funcArgExprs expectedArgTypes [1..]
920 if length funcArgTypes /= length expectedArgTypes - 1 then
921 throwE (AtomTypeCountError funcArgTypes expectedArgTypes)
923 either throwE pure (atomTypeVerify expectedType (last expectedArgTypes))
924verifyAtomExprTypes relIn (RelationAtomExpr relationExpr) expectedType = runExceptT $ do
926 relType <- either throwE pure (runReader (typeForRelationalExpr relationExpr) (mergeAttributesIntoRelationalExprState (attributes relIn) rstate))
927 either throwE pure (atomTypeVerify expectedType (RelationAtomType (attributes relType)))
928verifyAtomExprTypes rel cons@ConstructedAtomExpr{} expectedType = runExceptT $ do
929 cType <- liftE (typeFromAtomExpr (attributes rel) cons)
930 either throwE pure (atomTypeVerify expectedType cType)
932-- | Look up the type's name and create a new attribute.
933evalAttrExpr :: TypeConstructorMapping -> AttributeExpr -> Either RelationalError Attribute
934evalAttrExpr aTypes (AttributeAndTypeNameExpr attrName tCons ()) = do
935 aType <- atomTypeForTypeConstructorValidate True tCons aTypes M.empty
936 validateAtomType aType aTypes
937 Right (Attribute attrName aType)
939evalAttrExpr _ (NakedAttributeExpr attr) = Right attr
941evalTupleExpr :: Maybe Attributes -> TupleExpr -> RelationalExprState (Either RelationalError RelationTuple)
942evalTupleExpr mAttrs (TupleExpr tupMap) = do
943 context <- fmap stateElemsContext ask
945 -- it's not possible for AtomExprs in tuple constructors to reference other Attributes' atoms due to the necessary order-of-operations (need a tuple to pass to evalAtomExpr)- it may be possible with some refactoring of type usage or delayed evaluation- needs more thought, but not a priority
946 -- I could adjust this logic so that when the attributes are not specified (Nothing), then I can attempt to extract the attributes from the tuple- the type resolution will blow up if an ambiguous data constructor is used (Left 4) and this should allow simple cases to "relation{tuple{a 4}}" to be processed
947 let attrs = fromMaybe A.emptyAttributes mAttrs
948 attrAtoms <- mapM (\(attrName, aExpr) -> do
949 --provided when the relation header is available
950 let eExpectedAtomType = A.atomTypeForAttributeName attrName attrs
951 unresolvedType <- liftE (typeFromAtomExpr attrs aExpr)
952 resolvedType <- case eExpectedAtomType of
953 Left _ -> pure unresolvedType
954 Right typeHint -> either throwE pure (resolveAtomType typeHint unresolvedType)
955 --resolve atom typevars based on resolvedType?
956 newAtom <- liftE (evalAtomExpr emptyTuple aExpr)
957 pure (attrName, newAtom, resolvedType)
959 let tupAttrs = A.attributesFromList $ map (\(attrName, _, aType) -> Attribute attrName aType) attrAtoms
960 atoms = V.fromList $ map (\(_, atom, _) -> atom) attrAtoms
961 tup = mkRelationTuple tupAttrs atoms
962 tConss = typeConstructorMapping context
963 finalAttrs = fromMaybe tupAttrs mAttrs
964 --verify that the attributes match
965 when (A.attributeNameSet finalAttrs /= A.attributeNameSet tupAttrs) $ throwE (TupleAttributeTypeMismatchError tupAttrs)
966 tup' <- either throwE pure (resolveTypesInTuple finalAttrs tConss (reorderTuple finalAttrs tup))
967 _ <- either throwE pure (validateTuple tup' tConss)
970evalAttributeNames :: AttributeNames -> RelationalExpr -> RelationalExprState (Either RelationalError (S.Set AttributeName))
971evalAttributeNames attrNames expr = do
972 eExprType <- typeForRelationalExpr expr
974 Left err -> pure (Left err)
976 let typeNameSet = S.fromList (V.toList (A.attributeNames (attributes exprTyp)))
978 AttributeNames names ->
979 case A.projectionAttributesForNames names (attributes exprTyp) of
980 Left err -> pure (Left err)
981 Right attrs -> pure (Right (S.fromList (V.toList (A.attributeNames attrs))))
983 InvertedAttributeNames names -> do
984 let nonExistentAttributeNames = A.attributeNamesNotContained names typeNameSet
985 if not (S.null nonExistentAttributeNames) then
986 pure (Left (AttributeNamesMismatchError nonExistentAttributeNames))
988 pure (Right (A.nonMatchingAttributeNameSet names typeNameSet))
990 UnionAttributeNames namesA namesB -> do
991 eNameSetA <- evalAttributeNames namesA expr
993 Left err -> pure (Left err)
995 eNameSetB <- evalAttributeNames namesB expr
997 Left err -> pure (Left err)
999 pure (Right (S.union nameSetA nameSetB))
1001 IntersectAttributeNames namesA namesB -> do
1002 eNameSetA <- evalAttributeNames namesA expr
1004 Left err -> pure (Left err)
1005 Right nameSetA -> do
1006 eNameSetB <- evalAttributeNames namesB expr
1008 Left err -> pure (Left err)
1010 pure (Right (S.intersection nameSetA nameSetB))
1012 RelationalExprAttributeNames attrExpr -> do
1013 eAttrExprType <- typeForRelationalExpr attrExpr
1014 case eAttrExprType of
1015 Left err -> pure (Left err)
1016 Right attrExprType -> pure (Right (A.attributeNameSet (attributes attrExprType)))
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list