on master help sign in
fix confusing error message when creating incdep where the expressions do not have equal relation types
52a18c6AgentM
docs/tutd_tutorial.markdown+1−1
@@ -526,7 +526,7 @@ Foreign keys are constraints which require that certain values appearing in one
526TutorialD (master/main): foreign key s#_in_sp sp{s#} in s{s#}
526TutorialD (master/main): foreign key s#_in_sp sp{s#} in s{s#}
527```
527```
528
528
529−This expression ensure that any sp{s#} must also appear as a value in s{s#}.
529+This expression ensure that any sp{s#} must also appear as a value in s{s#}. Note that the sub-expression (on the left of "in") and the super-expression (on the right) must be of the same relation type- the generated relation values' attribute names and types must be equal. Use `rename` to make attribute names identical.
530
530
531#### Functional dependencies
531#### Functional dependencies
532
532
src/lib/ProjectM36/Error.hs+1−1
@@ -24,7 +24,7 @@ data RelationalError = NoSuchAttributeNamesError (S.Set AttributeName)
24 | CouldNotInferAttributes
24 | CouldNotInferAttributes
25 | RelVarNotDefinedError RelVarName
25 | RelVarNotDefinedError RelVarName
26 | RelVarAlreadyDefinedError RelVarName
26 | RelVarAlreadyDefinedError RelVarName
27− | RelVarAssignmentTypeMismatchError Attributes Attributes --expected, found
27+ | RelationTypeMismatchError Attributes Attributes --expected, found
28 | InclusionDependencyCheckError IncDepName
28 | InclusionDependencyCheckError IncDepName
29 | InclusionDependencyNameInUseError IncDepName
29 | InclusionDependencyNameInUseError IncDepName
30 | InclusionDependencyNameNotInUseError IncDepName
30 | InclusionDependencyNameNotInUseError IncDepName
src/lib/ProjectM36/RelationalExpression.hs+18−14
@@ -244,8 +244,8 @@ setRelVar relVarName rel = do
244 potentialContext = currentContext { relationVariables = newRelVars }
244 potentialContext = currentContext { relationVariables = newRelVars }
245
245
246 case checkConstraints potentialContext of
246 case checkConstraints potentialContext of
247− Just err -> pure (Left err)
247+ Left err -> pure (Left err)
248− Nothing -> do
248+ Right _ -> do
249 putStateContext potentialContext
249 putStateContext potentialContext
250 return (Right ())
250 return (Right ())
251
251
@@ -295,7 +295,7 @@ evalDatabaseContextExpr (Assign relVarName expr) = do
295 if A.attributesEqual expectedAttributes foundAttributes then
295 if A.attributesEqual expectedAttributes foundAttributes then
296 setRelVar relVarName rel
296 setRelVar relVarName rel
297 else
297 else
298− pure (Left (RelVarAssignmentTypeMismatchError expectedAttributes foundAttributes))
298+ pure (Left (RelationTypeMismatchError expectedAttributes foundAttributes))
299
299
300evalDatabaseContextExpr (Insert relVarName relExpr) = do
300evalDatabaseContextExpr (Insert relVarName relExpr) = do
301 context <- getStateContext
301 context <- getStateContext
@@ -359,8 +359,8 @@ evalDatabaseContextExpr (AddInclusionDependency newDepName newDep) = do
359 else do
359 else do
360 let potentialContext = currContext { inclusionDependencies = newDeps }
360 let potentialContext = currContext { inclusionDependencies = newDeps }
361 case checkConstraints potentialContext of
361 case checkConstraints potentialContext of
362− Just err -> pure (Left err)
362+ Left err -> pure (Left err)
363− Nothing -> do
363+ Right _ -> do
364 putStateContext potentialContext
364 putStateContext potentialContext
365 return (Right ())
365 return (Right ())
366
366
@@ -574,22 +574,26 @@ updateTupleWithAtomExprs exprMap context tupIn = do
574 pure (updateTupleWithAtoms (M.fromList atomsAssoc) tupIn)
574 pure (updateTupleWithAtoms (M.fromList atomsAssoc) tupIn)
575
575
576--run verification on all constraints
576--run verification on all constraints
577−checkConstraints :: DatabaseContext -> Maybe RelationalError
577+checkConstraints :: DatabaseContext -> Either RelationalError ()
578−checkConstraints context = case failures of
578+checkConstraints context = do
579− [] -> Nothing
579+ mapM_ (uncurry checkIncDep) (M.toList deps)
580− l:_ -> Just l
580+ pure ()
581 where
581 where
582− failures = M.elems $ M.mapMaybeWithKey checkIncDep deps
583 deps = inclusionDependencies context
582 deps = inclusionDependencies context
584− eval expr = runReader (evalRelationalExpr expr) (RelationalExprStateElems context)
583+ eval expr = evalReader (evalRelationalExpr expr)
584+ evalReader f = runReader f (RelationalExprStateElems context)
585 checkIncDep depName (InclusionDependency subsetExpr supersetExpr) = do
585 checkIncDep depName (InclusionDependency subsetExpr supersetExpr) = do
586+ --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.
587+ typeSub <- evalReader (typeForRelationalExpr subsetExpr)
588+ typeSuper <- evalReader (typeForRelationalExpr supersetExpr)
589+ when (typeSub /= typeSuper) (Left (RelationTypeMismatchError (attributes typeSub) (attributes typeSuper)))
586 let checkExpr = Equals supersetExpr (Union subsetExpr supersetExpr)
590 let checkExpr = Equals supersetExpr (Union subsetExpr supersetExpr)
587 case eval checkExpr of
591 case eval checkExpr of
588− Left err -> Just err
592+ Left err -> Left err
589 Right resultRel -> if resultRel == relationTrue then
593 Right resultRel -> if resultRel == relationTrue then
590− Nothing
594+ pure ()
591 else
595 else
592− Just $ InclusionDependencyCheckError depName
596+ Left (InclusionDependencyCheckError depName)
593
597
594-- 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
598-- 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
595-- furthermore, the type of a relational expression is the resultant header of the evaluated empty-tupled relation
599-- furthermore, the type of a relational expression is the resultant header of the evaluated empty-tupled relation
src/lib/ProjectM36/TransactionGraph.hs+2−2
@@ -407,8 +407,8 @@ mergeTransactions stamp newId parentId mergeStrategy (headNameA, headNameB) grap
407 case createMergeTransaction stamp newId mergeStrategy subGraph' (transA, transB) of
407 case createMergeTransaction stamp newId mergeStrategy subGraph' (transA, transB) of
408 Left err -> Left (MergeTransactionError err)
408 Left err -> Left (MergeTransactionError err)
409 Right mergedTrans -> case checkConstraints (concreteDatabaseContext mergedTrans) of
409 Right mergedTrans -> case checkConstraints (concreteDatabaseContext mergedTrans) of
410− Just err -> Left err
410+ Left err -> Left err
411− Nothing -> case headNameForTransaction disconParent graph of
411+ Right _ -> case headNameForTransaction disconParent graph of
412 Nothing -> Left (TransactionIsNotAHeadError parentId)
412 Nothing -> Left (TransactionIsNotAHeadError parentId)
413 Just headName -> do
413 Just headName -> do
414 (newTrans, newGraph) <- addTransactionToGraph headName mergedTrans graph
414 (newTrans, newGraph) <- addTransactionToGraph headName mergedTrans graph
test/Client/Simple.hs+1−1
@@ -55,7 +55,7 @@ testSimpleCommitFailure = TestCase $ do
55 execute $ Assign "x" (ExistingRelation relationTrue)
55 execute $ Assign "x" (ExistingRelation relationTrue)
56 --cause error
56 --cause error
57 execute $ Assign "x" (MakeStaticRelation failAttrs emptyTupleSet)
57 execute $ Assign "x" (MakeStaticRelation failAttrs emptyTupleSet)
58− let expectedErr = Left (RelError (RelVarAssignmentTypeMismatchError V.empty failAttrs))
58+ let expectedErr = Left (RelError (RelationTypeMismatchError V.empty failAttrs))
59 assertEqual "dbc error" expectedErr err
59 assertEqual "dbc error" expectedErr err
60
60
61
61