@@ -1,14 +1,14 @@
11-- this is an implementation of the Relation typeclass which creates a GADT DSL
22{-# LANGUAGE GADTs,ExistentialQuantification,NoImplicitPrelude #-}
33module ProjectM36.Relation where
4−import qualified Data.Map as M
54import qualified Data.Set as S
65import qualified Data.HashSet as HS
76import Control.Monad
7+import qualified Data.Vector as V
88
99import ProjectM36.Base
1010import ProjectM36.Tuple
11−import ProjectM36.Attribute
11+import qualified ProjectM36.Attribute as A
1212import ProjectM36.TupleSet
1313import ProjectM36.Error
1414import Prelude hiding (join)
@@ -17,48 +17,48 @@ attributes :: Relation -> Attributes
1717attributes (Relation attrs _ ) = attrs
1818
1919attributeNames :: Relation -> S.Set AttributeName
20−attributeNames (Relation attrs _) = M.keysSet attrs
20+attributeNames (Relation attrs _) = A.attributeNameSet attrs
2121
2222attributeForName :: AttributeName -> Relation -> Maybe Attribute
23−attributeForName attrName (Relation attrs _) = M.lookup attrName attrs
23+attributeForName attrName (Relation attrs _) = A.attributeForName attrName attrs
2424
2525attributesForNames :: S.Set AttributeName -> Relation -> Attributes
26−attributesForNames attrNameSet (Relation attrs _) = M.intersection attrs hollowMap
27− where
28− hollowMap = M.fromList $ zip (S.toList attrNameSet) (repeat (Attribute "" StringAtomType))
26+attributesForNames attrNameSet (Relation attrs _) = A.attributesForNames attrNameSet attrs
2927
30−attributeTypeForName :: AttributeName -> Relation -> Maybe AtomType
31−attributeTypeForName attrName rel = case attr of
32− (Just (Attribute _ atomType)) -> Just atomType
33− _ -> Nothing
34− where
35− attr = attributeForName attrName rel
28+atomTypeForName :: AttributeName -> Relation -> Maybe AtomType
29+atomTypeForName attrName (Relation attrs _) = A.atomTypeForAttributeName attrName attrs
3630
3731atomValueType :: Atom -> AtomType
3832atomValueType (StringAtom _) = StringAtomType
3933atomValueType (IntAtom _) = IntAtomType
4034atomValueType (RelationAtom rel) = RelationAtomType (attributes rel)
4135
36+mkRelationFromList :: Attributes -> [[Atom]] -> Either RelationalError Relation
37+mkRelationFromList attrs atomMatrix = do
38+ tupSet <- mkTupleSetFromList attrs atomMatrix
39+ return $ Relation attrs tupSet
40+
4241mkRelation :: Attributes -> RelationTupleSet -> Either RelationalError Relation
4342mkRelation attrs tupleSet = do
4443 --check that all tuples have the same keys
4544 --check that all tuples have keys (1-N) where N is the attribute count
46− case verifyRelationTupleSet attrs tupleSet of
45+ case verifyTupleSet attrs tupleSet of
4746 Left err -> Left err
4847 Right verifiedTupleSet -> return $ Relation attrs verifiedTupleSet
4948
5049relationTrue :: Relation
51−relationTrue = Relation M.empty $ HS.singleton (RelationTuple M.empty)
50+relationTrue = Relation A.emptyAttributes singletonTupleSet
5251
5352relationFalse :: Relation
54−relationFalse = Relation M.empty emptyTupleSet
53+relationFalse = Relation A.emptyAttributes emptyTupleSet
5554
5655madeTrue :: Either RelationalError Relation
57−madeTrue = mkRelation M.empty (HS.fromList [])
56+madeTrue = mkRelation A.emptyAttributes (HS.fromList [])
5857
5958madeFalse :: Either RelationalError Relation
60−madeFalse = mkRelation M.empty emptyTupleSet
59+madeFalse = mkRelation A.emptyAttributes emptyTupleSet
6160
61+{-
6262--used for predicate filtering- instead of allowing for tuple types to be floating around in Tutorial D, just created a "singleton value" relation with cardinality and arity 1 for tuple atom comparison purposes
6363singletonValueFromRelation :: Relation -> Either RelationalError Atom
6464singletonValueFromRelation rel@(Relation _ tupSet) = if cardinality rel /= Countable 1 then
@@ -71,6 +71,7 @@ singletonValueFromRelation rel@(Relation _ tupSet) = if cardinality rel /= Count
7171 value = snd $ head $ M.toList tupMap
7272 tupMap = tup2Map $ head (HS.toList tupSet)
7373 tup2Map (RelationTuple tupmap) = tupmap
74+-}
7475
7576union :: Relation -> Relation -> Either RelationalError Relation
7677union (Relation attrs1 tupSet1) (Relation attrs2 tupSet2) =
@@ -83,7 +84,7 @@ union (Relation attrs1 tupSet1) (Relation attrs2 tupSet2) =
8384
8485project :: S.Set AttributeName -> Relation -> Either RelationalError Relation
8586project projectionAttrNames rel =
86− if not $ attributeNamesContained projectionAttrNames (attributeNames rel)
87+ if not $ A.attributeNamesContained projectionAttrNames (attributeNames rel)
8788 then Left $ AttributeNameMismatchError ""
8889 else
8990 relFold folder (Right $ Relation newAttrs HS.empty) rel
@@ -94,12 +95,6 @@ project projectionAttrNames rel =
9495 Left err -> Left err
9596 Right acc2 -> union acc2 (Relation newAttrs (HS.singleton (tupleProject projectionAttrNames tupleToProject)))
9697
97−tupleProject :: S.Set AttributeName -> RelationTuple -> RelationTuple
98−tupleProject projectAttrs (RelationTuple tupleInMap) = RelationTuple $ M.filterWithKey attrFilter tupleInMap
99− where
100− attrFilter key _ = S.member key projectAttrs
101−
102−--the expensive rename is why I wanted to use IntMap instead of attribute names in the tupleset
10398rename :: AttributeName -> AttributeName -> Relation -> Either RelationalError Relation
10499rename oldAttrName newAttrName rel@(Relation oldAttrs oldTupSet) =
105100 if not attributeValid
@@ -109,15 +104,15 @@ rename oldAttrName newAttrName rel@(Relation oldAttrs oldTupSet) =
109104 else
110105 mkRelation newAttrs newTupSet
111106 where
112− newAttributeInUse = attributeNamesContained (S.singleton newAttrName) (attributeNames rel)
113− attributeValid = attributeNamesContained (S.singleton oldAttrName) (attributeNames rel)
114− newAttrs = M.delete oldAttrName $ M.insert newAttrName (renameAttribute newAttrName (oldAttrs M.! oldAttrName)) oldAttrs
107+ newAttributeInUse = A.attributeNamesContained (S.singleton newAttrName) (attributeNames rel)
108+ attributeValid = A.attributeNamesContained (S.singleton oldAttrName) (attributeNames rel)
109+ newAttrs = A.renameAttributes oldAttrName newAttrName oldAttrs
115110 newTupSet = HS.map tupsetmapper oldTupSet
116111 tupsetmapper tuple = tupleRenameAttribute oldAttrName newAttrName tuple
117112
118113--the algebra should return a relation of one attribute and one row with the arity
119114arity :: Relation -> Int
120−arity (Relation attrs _) = M.size attrs
115+arity (Relation attrs _) = A.arity attrs
121116
122117cardinality :: Relation -> RelationCardinality
123118cardinality (Relation _ tupSet) = Countable (HS.size tupSet)
@@ -151,31 +146,33 @@ group groupAttrNames newAttrName rel = do
151146 nonGroupProjection <- project nonGroupAttrNames rel
152147 relFold folder (Right $ Relation newAttrs emptyTupleSet) nonGroupProjection
153148 where
154− newAttrs = M.insert newAttrName groupAttr (attributesForNames nonGroupAttrNames rel)
149+ newAttrs = A.addAttribute groupAttr (attributesForNames nonGroupAttrNames rel)
155150 groupAttr = Attribute newAttrName (RelationAtomType (attributesForNames nonGroupAttrNames rel))
156− nonGroupAttrNames = nonMatchingAttributeNameSet groupAttrNames (attributeNames rel)
151+ nonGroupAttrNames = A.nonMatchingAttributeNameSet groupAttrNames (attributeNames rel)
157152 folder tupleIn acc = case acc of
158153 Left err -> Left err
159154 Right acc2 -> union acc2 (Relation newAttrs (HS.singleton (tupleExtend tupleIn (matchingRelTuple tupleIn))))
160155 matchingRelTuple groupTuple = case imageRelationFor groupTuple rel of
161− Right rel2 -> RelationTuple $ M.singleton newAttrName (RelationAtom rel2)
156+ Right rel2 -> RelationTuple (V.singleton groupAttr) (V.singleton (RelationAtom rel2))
162157 Left _ -> undefined
163158
164159--help restriction function
165160--returns a subrelation of
166161restrictEq :: RelationTuple -> Relation -> Either RelationalError Relation
167−restrictEq (RelationTuple tupMap) rel = restrict rfilter rel
162+restrictEq tuple rel = restrict rfilter rel
168163 where
169164 rfilter :: RelationTuple -> Bool
170− rfilter (RelationTuple tupMap2) = M.isSubmapOf tupMap tupMap2
165+ rfilter tupleIn = tupleIntersection tuple tupleIn /= emptyTuple
171166
172167-- unwrap relation-valued attribute
173168-- return error if relval attrs and nongroup attrs overlap
174169ungroup :: AttributeName -> Relation -> Either RelationalError Relation
175−ungroup relvalAttrName rel = relFold relFolder (Right $ Relation newAttrs emptyTupleSet) rel
176− where
177− newAttrs = M.union (attributesForRelval relvalAttrName rel) nonGroupAttrs
178− nonGroupAttrs = M.delete relvalAttrName (attributes rel)
170+ungroup relvalAttrName rel = case attributesForRelval relvalAttrName rel of
171+ Left err -> Left err
172+ Right relvalAttrs -> relFold relFolder (Right $ Relation newAttrs emptyTupleSet) rel
173+ where
174+ newAttrs = A.addAttributes relvalAttrs nonGroupAttrs
175+ nonGroupAttrs = A.deleteAttributeName relvalAttrName (attributes rel)
179176 relFolder :: RelationTuple -> Either RelationalError Relation -> Either RelationalError Relation
180177 relFolder tupleIn acc = case (tupleUngroup relvalAttrName newAttrs tupleIn) of
181178 Right ungroupRelation -> case acc of
@@ -187,36 +184,20 @@ ungroup relvalAttrName rel = relFold relFolder (Right $ Relation newAttrs emptyT
187184
188185--take an relval attribute name and a tuple and ungroup the relval
189186tupleUngroup :: AttributeName -> Attributes -> RelationTuple -> Either RelationalError Relation
190−tupleUngroup relvalAttrName newAttrs tuple@(RelationTuple tupMap) = case (tupMap M.! relvalAttrName) of
191− (RelationAtom relvalRelation) -> relFold folder (Right $ Relation newAttrs emptyTupleSet) relvalRelation
192− where
193− nonGroupTupleProjection = tupleProject nonGroupAttrNames tuple
194− nonGroupAttrNames = M.keysSet newAttrs
195− folder tupleIn acc = case acc of
196− Left err -> Left err
197− Right accRel -> union accRel $ Relation newAttrs (HS.singleton (tupleExtend nonGroupTupleProjection tupleIn))
198− relvalRelation = unsafeRelValFromAtom $ tupleAtomForAttribute tuple relvalAttrName
199− where
200− unsafeRelValFromAtom :: Atom -> Relation
201− unsafeRelValFromAtom (RelationAtom rel) = rel
202− unsafeRelValFromAtom (StringAtom _) = undefined
203− unsafeRelValFromAtom (IntAtom _) = undefined
204− _ -> Left $ AttributeIsNotRelationValuedError relvalAttrName
205−
206−--this is a hack needed because the relation attributes are untyped so we must dig into the relval to get the attribute names
207−attributesForRelval :: AttributeName -> Relation -> Attributes
208−attributesForRelval relvalAttrName (Relation _ tupleSet) = attributes relvalRelation
187+tupleUngroup relvalAttrName newAttrs tuple = do
188+ relvalRelation <- relationForAttributeName relvalAttrName tuple
189+ relFold folder (Right $ Relation newAttrs emptyTupleSet) relvalRelation
209190 where
210− --this is only temporarily needed until the Relation stores the types of the columns as data- hack alert
211− --find a tuple with attributes to steal to ungroup
212− oneTuple :: RelationTupleSet -> RelationTuple
213− oneTuple tupSet = head $ HS.toList $ HS.filter (\(RelationTuple tupMap) -> M.size tupMap > 0) tupSet
214− relvalRelation = unsafeRelValFromAtom $ tupleAtomForAttribute (oneTuple tupleSet) relvalAttrName
215− where
216− unsafeRelValFromAtom :: Atom -> Relation
217− unsafeRelValFromAtom (RelationAtom rel) = rel
218− unsafeRelValFromAtom (StringAtom _) = undefined
219− unsafeRelValFromAtom (IntAtom _) = undefined
191+ nonGroupTupleProjection = tupleProject nonGroupAttrNames tuple
192+ nonGroupAttrNames = A.attributeNameSet newAttrs
193+ folder tupleIn acc = case acc of
194+ Left err -> Left err
195+ Right accRel -> union accRel $ Relation newAttrs (HS.singleton (tupleExtend nonGroupTupleProjection tupleIn))
196+
197+attributesForRelval :: AttributeName -> Relation -> Either RelationalError Attributes
198+attributesForRelval relvalAttrName (Relation attrs _) = case A.atomTypeForAttributeName relvalAttrName attrs of
199+ (Just (RelationAtomType relAttrs)) -> Right relAttrs
200+ _ -> Left $ AttributeIsNotRelationValuedError relvalAttrName
220201
221202restrict :: (RelationTuple -> Bool) -> Relation -> Either RelationalError Relation
222203restrict rfilter (Relation attrs tupset) = Right $ Relation attrs $ HS.filter rfilter tupset
@@ -225,11 +206,11 @@ restrict rfilter (Relation attrs tupset) = Right $ Relation attrs $ HS.filter rf
225206--after changing from string atoms, there needs to be a type-checking step!
226207--this is a "nested loop" scan as described by the postgresql documentation
227208join :: Relation -> Relation -> Either RelationalError Relation
228−join (Relation attrs1 tupSet1) (Relation attrs2 tupSet2) = Right $ Relation newAttrs newTupSet
229− where
230− newAttrs = M.union attrs1 attrs2
231− newTupSet = HS.foldr tupleSetJoiner emptyTupleSet tupSet1
232− tupleSetJoiner tuple1 accumulator = HS.union (singleTupleSetJoin tuple1 tupSet2) accumulator
209+join (Relation attrs1 tupSet1) (Relation attrs2 tupSet2) = do
210+ let newTupSet = HS.foldr tupleSetJoiner emptyTupleSet tupSet1
211+ tupleSetJoiner tuple1 accumulator = HS.union (singleTupleSetJoin tuple1 tupSet2) accumulator
212+ newAttrs <- A.joinAttributes attrs1 attrs2
213+ return $ Relation newAttrs newTupSet
233214
234215--a map should NOT change the structure of a relation, so attributes should be constant
235216relMap :: (RelationTuple -> RelationTuple) -> Relation -> Either RelationalError Relation
@@ -242,7 +223,7 @@ relMap mapper (Relation attrs tupleSet) = do
242223 let remappedTuple = mapper tupleIn
243224 if tupleAttributes remappedTuple == tupleAttributes tupleIn
244225 then Right remappedTuple
245− else Left $ TupleAttributeTypeMismatchError (attributesDifference (tupleAttributes tupleIn) attrs)
226+ else Left $ TupleAttributeTypeMismatchError (A.attributesDifference (tupleAttributes tupleIn) attrs)
246227
247228--in the future, the mapper should be able to return a RelationalError likely
248229relMogrify :: (RelationTuple -> RelationTuple) -> Attributes -> Relation -> Either RelationalError Relation
@@ -258,12 +239,8 @@ relFold folder acc (Relation _ tupleSet) = HS.foldr folder acc tupleSet
258239imageRelationFor :: RelationTuple -> Relation -> Either RelationalError Relation
259240imageRelationFor matchTuple rel = do
260241 restricted <- restrictEq matchTuple rel --restrict across matching tuples
261− project (nonMatchingAttributeNameSet (attributeNames rel) (tupleAttributeNameSet matchTuple)) restricted --project across attributes not in rel
242+ project (A.nonMatchingAttributeNameSet (attributeNames rel) (tupleAttributeNameSet matchTuple)) restricted --project across attributes not in rel
262243
263−imageRelationTest :: Either RelationalError Relation
264−imageRelationTest = imageRelationFor tup s
265− where
266− tup = mkRelationTuple (S.fromList ["SNAME", "CITY", "S#"]) (M.fromList [("SNAME", StringAtom "Smith"), ("S#", StringAtom "S1"), ("CITY", StringAtom "London")])
267244
268245--returns a relation-valued attribute image relation for each tuple in rel1
269246--algorithm:
@@ -278,61 +255,15 @@ imageRelationJoin rel1@(Relation attrNameSet1 tupSet1) rel2@(Relation attrNameSe
278255 tupleSetJoiner tup1 acc = undefined
279256-}
280257
281−{- C.J. Date's canonical example relvars -}
282−
283−relvarS :: Either RelationalError Relation
284−relvarS = mkRelation attributeSet tupleSet
285− where
286− attributeSet = M.fromList [("S#", Attribute "S#" StringAtomType),
287− ("SNAME", Attribute "SNAME" StringAtomType),
288− ("STATUS", Attribute "STATUS" IntAtomType),
289− ("CITY", Attribute "CITY" StringAtomType)]
290− tupleSet = HS.fromList $ mkRelationTuples attributeSet [
291− M.fromList [("S#", StringAtom "S1") , ("SNAME", StringAtom "Smith"), ("STATUS", IntAtom 20) , ("CITY", StringAtom "London")],
292− M.fromList [("S#", StringAtom "S2"), ("SNAME", StringAtom "Jones"), ("STATUS", IntAtom 10), ("CITY", StringAtom "Paris")],
293− M.fromList [("S#", StringAtom "S3"), ("SNAME", StringAtom "Blake"), ("STATUS", IntAtom 30), ("CITY", StringAtom "Paris")],
294− M.fromList [("S#", StringAtom "S4"), ("SNAME", StringAtom "Clark"), ("STATUS", IntAtom 20), ("CITY", StringAtom "London")],
295− M.fromList [("S#", StringAtom "S5"), ("SNAME", StringAtom "Adams"), ("STATUS", IntAtom 30), ("CITY", StringAtom "Athens")]]
296−
297−s :: Relation
298−s = case relvarS of { Right r -> r }
299−
300−relvarP :: Either RelationalError Relation
301−relvarP = mkRelation attributeSet tupleSet
302− where
303− attributeSet = M.fromList [("P#", Attribute "P#" StringAtomType),
304− ("PNAME", Attribute "PNAME" StringAtomType),
305− ("COLOR", Attribute "COLOR" StringAtomType),
306− ("WEIGHT", Attribute "WEIGHT" StringAtomType),
307− ("CITY", Attribute "CITY" StringAtomType)]
308− tupleSet = HS.fromList $ mkRelationTuples attributeSet [
309− M.fromList [("P#", StringAtom "P1"), ("PNAME", StringAtom "Nut"), ("COLOR", StringAtom "Red"), ("WEIGHT", IntAtom 12), ("CITY", StringAtom "London")]
310− ]
311−p :: Relation
312−p = case relvarP of { Right r -> r }
313−
314−relvarSP :: Either RelationalError Relation
315−relvarSP = mkRelation attributeSet tupleSet
316− where
317− attributeSet = M.fromList [("S#", Attribute "S#" StringAtomType),
318− ("P#", Attribute "P#" StringAtomType),
319− ("QTY", Attribute "QTY" StringAtomType)]
320− tupleSet = HS.fromList $ mkRelationTuples attributeSet [
321− M.fromList [("S#", StringAtom "S1"), ("P#", StringAtom "P1"), ("QTY", IntAtom 300)]
322− ]
323−
324−sp :: Relation
325−sp = case relvarSP of { Right r -> r }
326−
327−
328258-- returns a relation with tupleCount tuples with a set of integer attributes attributesCount long
329259-- this is useful for performance and resource usage testing
330260matrixRelation :: Int -> Int -> Either RelationalError Relation
331−matrixRelation attributeCount tupleCount = mkRelation attributeSet tupleSet
332− where
333− attributeSet = M.fromList $ map (\c-> (show c, Attribute (show c) IntAtomType)) [0 .. attributeCount]
334− tupleSet = HS.fromList $ mkRelationTuples attributeSet $ map (\c -> tuple c) [0 .. tupleCount]
335− tuple tupleX = M.fromList $ map (\c -> (show c, IntAtom tupleX)) [0..attributeCount]
261+matrixRelation attributeCount tupleCount = do
262+ let attrs = A.attributesFromList $ map (\c-> Attribute (show c) IntAtomType) [0 .. attributeCount]
263+ tuple tupleX = RelationTuple attrs (V.generate attributeCount (\_ -> IntAtom tupleX))
264+ tupleSet <- mkTupleSet attrs $ map (\c -> tuple c) [0 .. tupleCount]
265+ mkRelation attrs tupleSet
266+
336267
337268
338269