on master help sign in
WIP attributes + hashset version and accessors refactoring

checkConstraints fail due to attributes mishandling somewhere

commitda70d101472c2dd2506a955ec85bea1c899ad674 authorAgentM <agentm@themactionfaction.com> dateSun 3 Jan 2021, 22:28 (6 years ago) parente125b9a
21 files changed+270−134
project-m36.cabal+12 src/bin/ProjectM36/Server/RemoteCallTypes/Json.hs+14−2 src/bin/TutorialD/Interpreter/Base.hs+3−3 src/bin/TutorialD/Printer.hs+6−6 src/lib/ProjectM36/Arbitrary.hs+1−1 src/lib/ProjectM36/AtomType.hs+7−7 src/lib/ProjectM36/Atomable.hs+1−1 src/lib/ProjectM36/Attribute.hs+117−30 src/lib/ProjectM36/Base.hs+24−16 src/lib/ProjectM36/DataFrame.hs+6−6 src/lib/ProjectM36/Relation.hs+2−2 src/lib/ProjectM36/Relation/Parse/CSV.hs+2−3 src/lib/ProjectM36/Relation/Show/CSV.hs+5−5 src/lib/ProjectM36/Relation/Show/HTML.hs+2−2 src/lib/ProjectM36/RelationalExpression.hs+9−6 src/lib/ProjectM36/Serialise/Base.hs+11−1 src/lib/ProjectM36/Tuple.hs+31−28 src/lib/ProjectM36/Tupleable.hs+10−8 test/Client/Simple.hs+1−2 test/Relation/Basic.hs+1−1 test/Relation/Tupleable.hs+5−4
project-m36.cabal+12
@@ -281,6 +281,18 @@ Benchmark basic-benchmark
281281 if flag(profiler)
282282 GHC-Prof-Options: -fprof-auto -rtsopts -threaded -Wall
283283
284+--benchmark inserts, updates, deletes for a basic use-case to shake out any space leaks
285+Benchmark update-exprs
286+ Default-Language: Haskell2010
287+ Default-Extensions: OverloadedStrings
288+ Build-Depends: base, criterion, project-m36, text, winery
289+ Main-Is: benchmark/Server.hs
290+ Type: exitcode-stdio-1.0
291+ GHC-Options: -Wall -threaded -rtsopts
292+ HS-Source-Dirs: ./src/bin
293+ if flag(profiler)
294+ GHC-Prof-Options: -fprof-auto -rtsopts -threaded -Wall
295+
284296Test-Suite test-tutoriald
285297 import: commontest
286298 type: exitcode-stdio-1.0
src/bin/ProjectM36/Server/RemoteCallTypes/Json.hs+14−2
@@ -11,6 +11,7 @@ import ProjectM36.Error
1111import ProjectM36.IsomorphicSchema
1212import ProjectM36.Server.RemoteCallTypes
1313import ProjectM36.MerkleHash
14+import ProjectM36.Attribute as A
1415
1516import Data.Aeson
1617import Data.ByteString.Base64 as B64
@@ -44,8 +45,11 @@ instance FromJSON ExecuteRelationalExpr
4445instance ToJSON Relation
4546instance FromJSON Relation
4647
47−instance ToJSON Attribute
48−instance FromJSON Attribute
48+instance ToJSON Attribute where
49+ toJSON (Attribute attrName aType) = object [ "name" .= attrName
50+ , "type" .= toJSON aType ]
51+instance FromJSON Attribute where
52+ parseJSON = withObject "Attribute" $ \v -> Attribute <$> v .: "name" <*> v .: "type"
4953
5054instance ToJSON ExtendTupleExpr
5155instance FromJSON ExtendTupleExpr
@@ -145,6 +149,12 @@ instance FromJSON MerkleHash where
145149 Left err -> fail ("Failed to parse merkle hash: " ++ err)
146150 Right bs -> pure (MerkleHash bs)
147151
152+instance ToJSON Attributes where
153+ toJSON attrs = object ["attributes" .= map toJSON (A.toList attrs)]
154+instance FromJSON Attributes where
155+ parseJSON = withObject "Attributes" $ \o ->
156+ A.attributesFromList <$> o .: "attributes"
157+
148158instance ToJSON RelationalError
149159instance FromJSON RelationalError
150160
@@ -168,3 +178,5 @@ instance FromJSON AtomFunctionError
168178
169179instance ToJSON WithNameExpr
170180instance FromJSON WithNameExpr
181+
182+
src/bin/TutorialD/Interpreter/Base.hs+3−3
@@ -12,6 +12,7 @@ module TutorialD.Interpreter.Base (
1212 where
1313import ProjectM36.Base
1414import ProjectM36.AtomType
15+import ProjectM36.Attribute as A
1516import ProjectM36.Relation
1617import ProjectM36.DataFrame
1718
@@ -30,7 +31,6 @@ import Data.Text hiding (count)
3031import System.Random
3132import qualified Data.Text as T
3233import qualified Data.List as L
33−import qualified Data.Vector as V
3434import qualified Data.Text.IO as TIO
3535import System.IO
3636import ProjectM36.Relation.Show.Term
@@ -163,8 +163,8 @@ uncapitalizedIdentifier =
163163showRelationAttributes :: Attributes -> Text
164164showRelationAttributes attrs = "{" <> T.concat (L.intersperse ", " $ L.map showAttribute attrsL) <> "}"
165165 where
166− showAttribute (Attribute name atomType) = name <> " " <> prettyAtomType atomType
167− attrsL = V.toList attrs
166+ showAttribute (Attribute name atomType') = name <> " " <> prettyAtomType atomType'
167+ attrsL = A.toList attrs
168168
169169type PromptLength = Int
170170
src/bin/TutorialD/Printer.hs+6−6
@@ -5,7 +5,7 @@
55{-# LANGUAGE TypeApplications #-}
66module TutorialD.Printer where
77import ProjectM36.Base
8−import ProjectM36.Attribute hiding (null)
8+import ProjectM36.Attribute as A hiding (null)
99import Data.Text.Prettyprint.Doc
1010import qualified Data.Set as S hiding (fromList)
1111import qualified Data.Vector as V
@@ -60,9 +60,9 @@ instance Pretty RelationTuple where
6060 pretty (RelationTuple attrs atoms) = "tuple" <> bracesList (zipWith (\x y-> pretty x <+> pretty y) (V.toList (attributeNames attrs)) (V.toList atoms))
6161
6262instance Pretty Relation where
63− pretty (Relation attrs tupSet) | attrs == V.empty && null (asList tupSet) = "false"
64− pretty (Relation attrs tupSet) | attrs == V.empty && asList tupSet == [RelationTuple V.empty V.empty] = "true"
65− pretty (Relation attrs tupSet) = "relation" <> prettyBracesList (V.toList attrs) <> prettyBracesList (asList tupSet)
63+ pretty (Relation attrs tupSet) | attrs == mempty && null (asList tupSet) = "false"
64+ pretty (Relation attrs tupSet) | attrs == mempty && asList tupSet == [RelationTuple mempty mempty] = "true"
65+ pretty (Relation attrs tupSet) = "relation" <> prettyBracesList (A.toList attrs) <> prettyBracesList (asList tupSet)
6666
6767instance Pretty Attribute where
6868 pretty (Attribute n aTy) = pretty n <+> pretty (show aTy) -- workaround
@@ -76,7 +76,7 @@ instance Pretty RelationalExpr where
7676 pretty (Extend ext r) = collectExtends r <> pretty ext <> "}"
7777 pretty (MakeRelationFromExprs Nothing (TupleExprs () tupExprs)) = "relation" <> prettyBracesList tupExprs
7878 pretty (MakeRelationFromExprs (Just attrExprs) (TupleExprs () tupExprs)) = "relation" <> prettyBracesList attrExprs <> prettyBracesList tupExprs
79− pretty (MakeStaticRelation attrs tupSet) = "relation" <> prettyBracesList (V.toList attrs) <> prettyBracesList (asList tupSet)
79+ pretty (MakeStaticRelation attrs tupSet) = "relation" <> prettyBracesList (A.toList attrs) <> prettyBracesList (asList tupSet)
8080 pretty (Union a b) = parens $ pretty' a <+> "union" <+> pretty' b
8181 pretty (Join a b) = parens $ pretty' a <+> "join" <+> pretty' b
8282 pretty (Rename n1 n2 relExpr) = parens $ pretty relExpr <+> "rename" <+> braces (pretty n1 <+> "as" <+> pretty n2)
@@ -130,7 +130,7 @@ instance Pretty AtomType where
130130 pretty DateTimeAtomType = "DateTime"
131131 pretty ByteStringAtomType = "ByteString"
132132 pretty BoolAtomType = "Bool"
133− pretty (RelationAtomType attrs) = "relation " <+> prettyBracesList (V.toList attrs)
133+ pretty (RelationAtomType attrs) = "relation " <+> prettyBracesList (A.toList attrs)
134134 pretty (ConstructedAtomType tcName tvMap) = pretty tcName <+> hsep (map pretty (M.toList tvMap)) --order matters
135135 pretty RelationalExprAtomType = "RelationalExpr"
136136 pretty (TypeVariableType x) = pretty x
src/lib/ProjectM36/Arbitrary.hs+1−1
@@ -84,7 +84,7 @@ maybeToRight y Nothing = Left y
8484arbitraryRelationTuple :: Attributes -> WithTCMap Gen (Either RelationalError RelationTuple)
8585arbitraryRelationTuple attris = do
8686 tcMap <- ask
87− listOfMaybeAType <- lift $ mapM ((\aTy -> runReaderT (arbitrary' aTy) tcMap) . atomType) (V.toList attris)
87+ listOfMaybeAType <- lift $ mapM ((\aTy -> runReaderT (arbitrary' aTy) tcMap) . atomType) (V.toList (attributesVec attris))
8888 case sequence listOfMaybeAType of
8989 Left err -> pure $ Left err
9090 Right listOfAttr -> do
src/lib/ProjectM36/AtomType.hs+7−7
@@ -211,7 +211,7 @@ isValidAtomTypeForTypeConstructor (ConstructedAtomType tConsName _) (ADTypeConst
211211
212212isValidAtomTypeForTypeConstructor (RelationAtomType attrs) (RelationAtomTypeConstructor attrExprs) tConsMap = do
213213 evaldAtomTypes <- mapM (\expr -> atomTypeForAttributeExpr expr tConsMap M.empty) attrExprs
214− mapM_ (uncurry resolveAtomType) (zip (map A.atomType (V.toList attrs)) evaldAtomTypes)
214+ mapM_ (uncurry resolveAtomType) (zip (A.atomTypesList attrs) evaldAtomTypes)
215215isValidAtomTypeForTypeConstructor aType tCons _ = Left (AtomTypeTypeConstructorReconciliationError aType (TC.name tCons))
216216
217217findTypeConstructor :: TypeConstructorName -> TypeConstructorMapping -> Maybe (TypeConstructorDef, [DataConstructorDef])
@@ -295,7 +295,7 @@ resolveTypeInAtom _ atom _ = Right atom
295295-- Example: "Nothing" does not specify the the argument in "Maybe a", so allow delayed resolution in the tuple before it is added to the relation. Note that this resolution could cause a type error. Hardly a Hindley-Milner system.
296296resolveTypesInTuple :: Attributes -> TypeConstructorMapping -> RelationTuple -> Either RelationalError RelationTuple
297297resolveTypesInTuple resolvedAttrs tConss (RelationTuple _ tupAtoms) = do
298− newAtoms <- mapM (\(atom, resolvedType) -> resolveTypeInAtom resolvedType atom tConss) (zip (V.toList tupAtoms) $ map A.atomType (V.toList resolvedAttrs))
298+ newAtoms <- mapM (\(atom, resolvedType) -> resolveTypeInAtom resolvedType atom tConss) (zip (V.toList tupAtoms) $ A.atomTypesList resolvedAttrs)
299299 Right (RelationTuple resolvedAttrs (V.fromList newAtoms))
300300
301301-- | Validate that the type is provided with complete type variables for type constructors.
@@ -314,7 +314,7 @@ validateAtomType typ@(ConstructedAtomType tConsName tVarMap) tConss =
314314 _ -> Right ()
315315validateAtomType (RelationAtomType attrs) tConss =
316316 mapM_ (\attr ->
317− validateAtomType (A.atomType attr) tConss) (V.toList attrs)
317+ validateAtomType (A.atomType attr) tConss) (attributesVec attrs)
318318validateAtomType (TypeVariableType x) _ = Left (TypeConstructorTypeVarMissing x)
319319validateAtomType _ _ = pure ()
320320
@@ -325,7 +325,7 @@ validateAttributes tConss attrs =
325325 else
326326 pure ()
327327 where
328− errs = lefts $ map ((`validateAtomType` tConss) . A.atomType) (V.toList attrs)
328+ errs = lefts $ map (`validateAtomType` tConss) (A.atomTypesList attrs)
329329
330330--ensure that all type vars are fully resolved
331331validateTypeVarMap :: TypeVarMap -> TypeConstructorMapping -> Either RelationalError ()
@@ -358,7 +358,7 @@ atomTypeVerify x@(RelationAtomType attrs1) y@(RelationAtomType attrs2) = do
358358 if notElem "_" [name1, name2] && name1 /= name2 then
359359 Left $ AtomTypeMismatchError x y
360360 else
361− atomTypeVerify (A.atomType attr1) (A.atomType attr2)) $ V.toList (V.zip attrs1 attrs2)
361+ atomTypeVerify (A.atomType attr1) (A.atomType attr2)) $ V.toList (V.zip (attributesVec attrs1) (attributesVec attrs2))
362362 return x
363363atomTypeVerify x y = if x == y then
364364 Right x
@@ -375,7 +375,7 @@ typeVarMapsVerify a b = M.keysSet a == M.keysSet b && (length . rights) zipped =
375375 (M.toAscList b)
376376
377377prettyAtomType :: AtomType -> T.Text
378−prettyAtomType (RelationAtomType attrs) = "relation {" `T.append` T.intercalate "," (map prettyAttribute (V.toList attrs)) `T.append` "}"
378+prettyAtomType (RelationAtomType attrs) = "relation {" `T.append` T.intercalate "," (map prettyAttribute (V.toList (attributesVec attrs))) `T.append` "}"
379379prettyAtomType (ConstructedAtomType tConsName typeVarMap) = tConsName `T.append` T.concat (map showTypeVars (M.toList typeVarMap))
380380 where
381381 showTypeVars (_, TypeVariableType x) = " " <> x
@@ -448,7 +448,7 @@ isResolvedType typ =
448448 TypeVariableType _ -> False
449449
450450isResolvedAttributes :: Attributes -> Bool
451−isResolvedAttributes attrs = all isResolvedAttribute (V.toList attrs)
451+isResolvedAttributes attrs = all isResolvedAttribute (V.toList (attributesVec attrs))
452452
453453isResolvedAttribute :: Attribute -> Bool
454454isResolvedAttribute = isResolvedType . A.atomType
src/lib/ProjectM36/Atomable.hs+1−1
@@ -237,7 +237,7 @@ typeToTypeConstructor x@ByteStringAtomType = PrimitiveTypeConstructor "ByteStrin
237237typeToTypeConstructor x@BoolAtomType = PrimitiveTypeConstructor "Bool" x
238238typeToTypeConstructor x@RelationalExprAtomType = PrimitiveTypeConstructor "RelationalExpr" x
239239typeToTypeConstructor (RelationAtomType attrs)
240− = RelationAtomTypeConstructor $ map attrToAttrExpr $ V.toList attrs
240+ = RelationAtomTypeConstructor $ map attrToAttrExpr $ V.toList (attributesVec attrs)
241241 where
242242 attrToAttrExpr (Attribute n t) = AttributeAndTypeNameExpr n (typeToTypeConstructor t) ()
243243typeToTypeConstructor (ConstructedAtomType tcName tvMap)
src/lib/ProjectM36/Attribute.hs+117−30
@@ -1,3 +1,4 @@
1+{-# OPTIONS_GHC -fno-warn-orphans #-}
12module ProjectM36.Attribute where
23import ProjectM36.Base
34import ProjectM36.Error
@@ -10,16 +11,45 @@ import qualified Data.Map as M
1011import Data.Either
1112
1213arity :: Attributes -> Int
13−arity = V.length
14+arity a = V.length (attributesVec a)
15+
16+instance Semigroup Attributes where
17+ attrsA <> attrsB =
18+ case joinAttributes attrsA attrsB of
19+ Left err -> error (show err)
20+ Right attrs' -> attrs'
21+
22+instance Monoid Attributes where
23+ mempty = Attributes mempty mempty
1424
1525emptyAttributes :: Attributes
16−emptyAttributes = V.empty
26+emptyAttributes = mempty
1727
1828null :: Attributes -> Bool
19−null = V.null
29+null a = V.null (attributesVec a)
30+
31+singleton :: Attribute -> Attributes
32+singleton attr = Attributes (V.singleton attr) (HS.singleton attr)
33+
34+toList :: Attributes -> [Attribute]
35+toList attrs = V.toList (attributesVec attrs)
2036
2137attributesFromList :: [Attribute] -> Attributes
22−attributesFromList = V.fromList -- . L.nub --too expensive
38+attributesFromList attrsL = Attributes vec hset
39+ where
40+ vec = if length attrsL == HS.size hset then
41+ --fast path- no duplicates
42+ V.fromList attrsL
43+ else
44+ --duplicate detected, uniqueify while maintaining original ordering
45+ V.fromList uniquedL
46+ hset = HS.fromList attrsL
47+ uniquedL = fst $ foldr (\attr acc@(l,s) ->
48+ if HS.member attr s then
49+ acc
50+ else
51+ (l ++ [attr], HS.insert attr s))
52+ ([],mempty) attrsL
2353
2454attributeName :: Attribute -> AttributeName
2555attributeName (Attribute name _) = name
@@ -28,17 +58,16 @@ atomType :: Attribute -> AtomType
2858atomType (Attribute _ atype) = atype
2959
3060atomTypes :: Attributes -> V.Vector AtomType
31−atomTypes = V.map atomType
61+atomTypes attrs = V.map atomType (attributesVec attrs)
3262
3363atomTypesList :: Attributes -> [AtomType]
3464atomTypesList = V.toList . atomTypes
3565
36−--hm- no error-checking here
3766addAttribute :: Attribute -> Attributes -> Attributes
38−addAttribute attr attrs = attrs `V.snoc` attr
67+addAttribute attr attrs = attrs <> singleton attr
3968
4069--if some attribute names overlap but the types do not, then spit back an error
41−joinAttributes :: Attributes -> Attributes -> Either RelationalError Attributes
70+{-joinAttributes :: Attributes -> Attributes -> Either RelationalError Attributes
4271joinAttributes attrs1 attrs2 | V.length uniqueOverlappingAttributes /= V.length overlappingAttributes = Left (TupleAttributeTypeMismatchError overlappingAttributes)
4372 | V.length overlappingAttrsDifferentTypes > 0 = Left (TupleAttributeTypeMismatchError overlappingAttrsDifferentTypes)
4473 | otherwise = Right $ vectorUniqueify (attrs1 V.++ attrs2)
@@ -47,20 +76,47 @@ joinAttributes attrs1 attrs2 | V.length uniqueOverlappingAttributes /= V.length
4776 attrNames2 = V.map attributeName attrs2
4877 uniqueOverlappingAttributes = vectorUniqueify overlappingAttributes
4978 overlappingAttributes = V.filter (`V.elem` attrs2) attrs1
79+-}
80+joinAttributes :: Attributes -> Attributes -> Either RelationalError Attributes
81+joinAttributes attrs1 attrs2 =
82+ if S.size overlappingNames == 0 then -- fast path, no overlapping names
83+ pure (concated id)
84+ else if attributesForNames overlappingNames attrs1 == attributesForNames overlappingNames attrs2 then -- check that atomtypes match
85+ pure (concated vectorUniqueify)
86+ else
87+ --special handling to validate that overlapping names have the same atom types
88+ Left (TupleAttributeTypeMismatchError (attributesForNames overlappingNames attrs1))
89+ where
90+ nameSet1 = attributeNameSet attrs1
91+ nameSet2 = attributeNameSet attrs2
92+ overlappingNames = S.intersection nameSet1 nameSet2
93+ concated f = Attributes (f (attributesVec attrs1 <> attributesVec attrs2)) (attributesSet attrs1 <> attributesSet attrs2)
5094
5195addAttributes :: Attributes -> Attributes -> Attributes
52−addAttributes = (V.++)
96+addAttributes = (<>)
97+
98+member :: Attribute -> Attributes -> Bool
99+member attr attrs = HS.member attr (attributesSet attrs)
53100
54101deleteAttributeName :: AttributeName -> Attributes -> Attributes
55−deleteAttributeName attrName = V.filter (\attr -> attributeName attr /= attrName)
102+deleteAttributeName attrName attrs = deleteAttributeNames (S.singleton attrName) attrs
103+
104+deleteAttributeNames :: S.Set AttributeName -> Attributes -> Attributes
105+deleteAttributeNames attrNames attrs = Attributes vec hset
106+ where
107+ vec = V.filter attrFilter (attributesVec attrs)
108+ hset = HS.filter attrFilter (attributesSet attrs)
109+ attrFilter attr = S.notMember (attributeName attr) attrNames
56110
57111renameAttribute :: AttributeName -> Attribute -> Attribute
58112renameAttribute newAttrName (Attribute _ typeo) = Attribute newAttrName typeo
59113
60114renameAttributes :: AttributeName -> AttributeName -> Attributes -> Attributes
61−renameAttributes oldAttrName newAttrName = V.map renamer
115+renameAttributes oldAttrName newAttrName attrs = Attributes vec hset
62116 where
63− renamer attr = if attributeName attr == oldAttrName then
117+ vec = V.map renamer (attributesVec attrs)
118+ hset = HS.map renamer (attributesSet attrs)
119+ renamer attr = if attributeName attr == oldAttrName then
64120 renameAttribute newAttrName attr
65121 else
66122 attr
@@ -71,7 +127,7 @@ atomTypeForAttributeName attrName attrs = do
71127 return atype
72128
73129attributeForName :: AttributeName -> Attributes -> Either RelationalError Attribute
74−attributeForName attrName attrs = case V.find (\attr -> attributeName attr == attrName) attrs of
130+attributeForName attrName attrs = case V.find (\attr -> attributeName attr == attrName) (attributesVec attrs) of
75131 Nothing -> Left (NoSuchAttributeNamesError (S.singleton attrName))
76132 Just attr -> Right attr
77133
@@ -89,15 +145,18 @@ projectionAttributesForNames names attrsIn =
89145 missingNames = attributeNamesNotContained names (S.fromList (V.toList (attributeNames attrsIn)))
90146
91147attributesForNames :: S.Set AttributeName -> Attributes -> Attributes
92−attributesForNames attrNameSet = V.filter filt
148+attributesForNames attrNameSet attrs = Attributes vec hset
149+
93150 where
151+ vec = V.filter filt (attributesVec attrs)
152+ hset = HS.filter filt (attributesSet attrs)
94153 filt attr = S.member (attributeName attr) attrNameSet
95154
96155attributeNameSet :: Attributes -> S.Set AttributeName
97−attributeNameSet attrVec = S.fromList $ V.toList $ V.map (\(Attribute name _) -> name) attrVec
156+attributeNameSet attrs = S.fromList $ V.toList $ V.map (\(Attribute name _) -> name) (attributesVec attrs)
98157
99158attributeNames :: Attributes -> V.Vector AttributeName
100−attributeNames = V.map attributeName
159+attributeNames attrs = V.map attributeName (attributesVec attrs)
101160
102161--checks if set s1 is wholly contained in the set s2
103162attributesContained :: Attributes -> Attributes -> Bool
@@ -118,42 +177,70 @@ attributeNamesNotContained subset superset = S.filter (`S.notMember` superset) s
118177
119178-- useful for display
120179orderedAttributes :: Attributes -> [Attribute]
121−orderedAttributes attrs = L.sortBy (\a b -> attributeName a `compare` attributeName b) (V.toList attrs)
180+orderedAttributes attrs = L.sortBy (\a b -> attributeName a `compare` attributeName b) (V.toList (attributesVec attrs))
122181
123182orderedAttributeNames :: Attributes -> [AttributeName]
124183orderedAttributeNames attrs = map attributeName (orderedAttributes attrs)
125184
126185-- take two attribute sets and return an attribute set with the attributes which do not match
186+--this is the function which benefits the most from the HashSet representation- this turned up in the insert performance test
127187attributesDifference :: Attributes -> Attributes -> Attributes
128−attributesDifference attrsA attrsB = V.fromList $ diff (V.toList attrsA) (V.toList attrsB)
188+{-attributesDifference attrsA attrsB = V.fromList $ diff (V.toList attrsA) (V.toList attrsB)
129189 where
130190 diff a b = (a L.\\ b) ++ (b L.\\ a)
191+-}
192+attributesDifference attrsA attrsB =
193+ if attributesSet attrsA == attributesSet attrsB then
194+ mempty
195+ else
196+ Attributes vec hset
197+ where
198+ hset = HS.difference setA setB <> HS.difference setB setA
199+ setA = attributesSet attrsA
200+ setB = attributesSet attrsB
201+ vec = V.filter (\attr -> HS.member attr hset) (attributesVec attrsA <> attributesVec attrsB)
131202
132203vectorUniqueify :: (Hash.Hashable a, Eq a) => V.Vector a -> V.Vector a
133204vectorUniqueify vecIn = V.fromList $ HS.toList $ HS.fromList $ V.toList vecIn
134205
135206--check that each attribute only appears once
136207verifyAttributes :: Attributes -> Either RelationalError Attributes
137−verifyAttributes attrs = if collapsedAttrs /= attrs then
138− Left (TupleAttributeTypeMismatchError (attributesDifference collapsedAttrs attrs))
139− else
140− Right attrs
208+verifyAttributes attrs =
209+ if vecSet == attributesSet attrs then
210+ pure attrs
211+ else
212+ Left (TupleAttributeTypeMismatchError diffAttrs)
141213 where
142− collapsedAttrs = vectorUniqueify attrs
143−
214+ vecSet = V.foldr' HS.insert HS.empty (attributesVec attrs)
215+ diffSet = HS.difference vecSet (attributesSet attrs) <> HS.difference (attributesSet attrs) vecSet
216+ diffAttrs = Attributes (V.fromList (HS.toList diffSet)) diffSet
217+
218+--used in Generics derivation for ADTs without named attributes- not to be used elsewhere
219+--drop first n attributes from vector representation
220+drop :: Int -> Attributes -> Attributes
221+drop c attrs = Attributes vec hset
222+ where
223+ droppedAttrs = V.take c (attributesVec attrs)
224+ vec = V.drop c (attributesVec attrs)
225+ hset = HS.filter (`V.notElem` droppedAttrs) hset
226+
227+-- use this in preference to (==) when the attribute ordering matters such as during tuple unions
144228attributesEqual :: Attributes -> Attributes -> Bool
145−attributesEqual attrs1 attrs2 = V.null (attributesDifference attrs1 attrs2)
229+attributesEqual a b = attributesVec a == attributesVec b
146230
147231attributesAsMap :: Attributes -> M.Map AttributeName Attribute
148−attributesAsMap attrs = (M.fromList . V.toList) (V.map (\attr -> (attributeName attr, attr)) attrs)
232+attributesAsMap attrs = V.foldr' (\attr acc -> M.insert (attributeName attr) attr acc) mempty (attributesVec attrs)
233+
149234
150235-- | Left-biased union of attributes.
151236union :: Attributes -> Attributes -> Attributes
152−union attrsA attrsB = V.fromList (M.elems unioned)
237+union attrsA attrsB = Attributes vec hset
153238 where
154− unioned = M.union (attributesAsMap attrsA) (attributesAsMap attrsB)
239+ hset = HS.union (attributesSet attrsA) (attributesSet attrsB)
240+ vec = HS.foldr (flip V.snoc) mempty hset
155241
156242intersection :: Attributes -> Attributes -> Attributes
157−intersection attrsA attrsB = V.fromList (M.elems intersected)
243+intersection attrsA attrsB = Attributes vec hset
158244 where
159− intersected = M.intersection (attributesAsMap attrsA) (attributesAsMap attrsB)
245+ hset = HS.intersection (attributesSet attrsA) (attributesSet attrsB)
246+ vec = HS.foldr (flip V.snoc) mempty hset
src/lib/ProjectM36/Base.hs+24−16
@@ -99,16 +99,23 @@ instance Hashable Attribute where
9999 hashWithSalt salt (Attribute attrName _) = hashWithSalt salt attrName
100100
101101-- | 'Attributes' represent the head of a relation.
102−type Attributes = V.Vector Attribute
102+data Attributes = Attributes (V.Vector Attribute) (HS.HashSet Attribute)
103+ deriving (NFData, Read, Hashable, Generic, Show)
104+
105+{-instance Show Attributes where
106+ show attrs = "attributesFromList [" <> L.intercalate " " (map (\attr -> "(" <> show attr <> ")") (V.toList (attributesVec attrs))) <> "]"
107+-}
108+attributesVec :: Attributes -> V.Vector Attribute
109+attributesVec (Attributes attrsVec _) = attrsVec
110+
111+attributesSet :: Attributes -> HS.HashSet Attribute
112+attributesSet (Attributes _ hs) = hs
113+
114+instance Eq Attributes where
115+ (Attributes _ a) == (Attributes _ b) = a == b
103116
104−-- | Equality function for a set of attributes.
105−attributesEqual :: Attributes -> Attributes -> Bool
106−attributesEqual attrs1 attrs2 = attrsAsSet attrs1 == attrsAsSet attrs2
107− where
108− attrsAsSet = HS.fromList . V.toList
109−
110117sortedAttributesIndices :: Attributes -> [(Int, Attribute)]
111−sortedAttributesIndices attrs = L.sortBy (\(_, Attribute name1 _) (_,Attribute name2 _) -> compare name1 name2) $ V.toList (V.indexed attrs)
118+sortedAttributesIndices (Attributes attrs _) = L.sortBy (\(_, Attribute name1 _) (_,Attribute name2 _) -> compare name1 name2) $ V.toList (V.indexed attrs)
112119
113120-- | The relation's tuple set is the body of the relation.
114121newtype RelationTupleSet = RelationTupleSet { asList :: [RelationTuple] } deriving (Hashable, Show, Generic, Read)
@@ -127,8 +134,8 @@ instance NFData RelationTupleSet where rnf = genericRnf
127134instance Hashable RelationTuple where
128135 --sanity check the tuple for attribute and tuple counts
129136 --this bit me when tuples were being hashed before being verified
130− hashWithSalt salt (RelationTuple attrs tupVec) = if V.length attrs /= V.length tupVec then
131− error "invalid tuple: attributes and tuple count mismatch"
137+ hashWithSalt salt (RelationTuple attrs tupVec) = if V.length (attributesVec attrs) /= V.length tupVec then
138+ error ("invalid tuple: attributes and tuple count mismatch " <> show (attributesVec attrs, tupVec))
132139 else
133140 salt `hashWithSalt`
134141 sortedAttrs `hashWithSalt`
@@ -142,19 +149,20 @@ instance Hashable RelationTuple where
142149data RelationTuple = RelationTuple Attributes (V.Vector Atom) deriving (Show, Read, Generic)
143150
144151instance Eq RelationTuple where
145− (==) tuple1@(RelationTuple attrs1 _) tuple2@(RelationTuple attrs2 _) = attributesEqual attrs1 attrs2 && atomsEqual
152+ tuple1@(RelationTuple attrs1 _) == tuple2@(RelationTuple attrs2 _) =
153+ attrs1 == attrs2 && atomsEqual
146154 where
147− atomForAttribute attr (RelationTuple attrs tupVec) = case V.findIndex (== attr) attrs of
155+ atomForAttribute attr (RelationTuple (Attributes attrs _) tupVec) = case V.findIndex (== attr) attrs of
148156 Nothing -> Nothing
149157 Just index -> tupVec V.!? index
150− atomsEqual = V.all (== True) $ V.map (\attr -> atomForAttribute attr tuple1 == atomForAttribute attr tuple2) attrs1
158+ atomsEqual = V.all (== True) $ V.map (\attr -> atomForAttribute attr tuple1 == atomForAttribute attr tuple2) (attributesVec attrs1)
151159
152160instance NFData RelationTuple where rnf = genericRnf
153161
154162data Relation = Relation Attributes RelationTupleSet deriving (Show, Generic,Typeable)
155163
156164instance Eq Relation where
157− Relation attrs1 tupSet1 == Relation attrs2 tupSet2 = attributesEqual attrs1 attrs2 && tupSet1 == tupSet2
165+ Relation attrs1 tupSet1 == Relation attrs2 tupSet2 = attrs1 == attrs2 && tupSet1 == tupSet2
158166
159167instance NFData Relation where rnf = genericRnf
160168
@@ -598,7 +606,7 @@ attrTypeVars (Attribute _ aType) = case aType of
598606 ByteStringAtomType -> S.empty
599607 BoolAtomType -> S.empty
600608 RelationalExprAtomType -> S.empty
601− (RelationAtomType attrs) -> S.unions (map attrTypeVars (V.toList attrs))
609+ (RelationAtomType attrs) -> S.unions (map attrTypeVars (V.toList (attributesVec attrs)))
602610 (ConstructedAtomType _ tvMap) -> M.keysSet tvMap
603611 (TypeVariableType nam) -> S.singleton nam
604612
@@ -622,7 +630,7 @@ atomTypeVars DateTimeAtomType = S.empty
622630atomTypeVars ByteStringAtomType = S.empty
623631atomTypeVars BoolAtomType = S.empty
624632atomTypeVars RelationalExprAtomType = S.empty
625−atomTypeVars (RelationAtomType attrs) = S.unions (map attrTypeVars (V.toList attrs))
633+atomTypeVars (RelationAtomType attrs) = S.unions (map attrTypeVars (V.toList (attributesVec attrs)))
626634atomTypeVars (ConstructedAtomType _ tvMap) = M.keysSet tvMap
627635atomTypeVars (TypeVariableType nam) = S.singleton nam
628636
src/lib/ProjectM36/DataFrame.hs+6−6
@@ -2,7 +2,7 @@
22{- A dataframe is a strongly-typed, ordered list of named tuples. A dataframe differs from a relation in that its tuples are ordered.-}
33module ProjectM36.DataFrame where
44import ProjectM36.Base
5−import ProjectM36.Attribute as A
5+import ProjectM36.Attribute as A hiding (drop)
66import ProjectM36.Error
77import qualified ProjectM36.Relation as R
88import ProjectM36.Relation.Show.Term
@@ -81,7 +81,7 @@ compareTupleByOneAttributeName attr tuple1 tuple2 =
8181 Right atom2 -> compareAtoms atom1 atom2
8282
8383atomForAttributeName :: AttributeName -> DataFrameTuple -> Either RelationalError Atom
84−atomForAttributeName attrName (DataFrameTuple tupAttrs tupVec) = case V.findIndex (\attr -> attributeName attr == attrName) tupAttrs of
84+atomForAttributeName attrName (DataFrameTuple tupAttrs tupVec) = case V.findIndex (\attr -> attributeName attr == attrName) (attributesVec tupAttrs) of
8585 Nothing -> Left (NoSuchAttributeNamesError (S.singleton attrName))
8686 Just index -> case tupVec V.!? index of
8787 Nothing -> Left (NoSuchAttributeNamesError (S.singleton attrName))
@@ -134,12 +134,12 @@ data DataFrameExpr = DataFrameExpr {
134134dataFrameAsHTML :: DataFrame -> T.Text
135135-- web browsers don't display tables with empty cells or empty headers, so we have to insert some placeholders- it's not technically the same, but looks as expected in the browser
136136dataFrameAsHTML df
137− | length (tuples df) == 1 && L.null (attributes df) = style <>
137+ | length (tuples df) == 1 && A.null (attributes df) = style <>
138138 tablestart <>
139139 "<tr><th></th></tr>" <>
140140 "<tr><td></td></tr>" <>
141141 tablefooter <> "</table>"
142− | L.null (tuples df) && L.null (attributes df) = style <>
142+ | L.null (tuples df) && A.null (attributes df) = style <>
143143 tablestart <>
144144 "<tr><th></th></tr>" <>
145145 tablefooter <>
@@ -162,7 +162,7 @@ tuplesAsHTML = foldr folder ""
162162 folder tuple acc = acc <> tupleAsHTML tuple
163163
164164tupleAssocs :: DataFrameTuple -> [(AttributeName, Atom)]
165−tupleAssocs (DataFrameTuple attrVec tupVec) = V.toList $ V.map (first attributeName) (V.zip attrVec tupVec)
165+tupleAssocs (DataFrameTuple attrs tupVec) = V.toList $ V.map (first attributeName) (V.zip (attributesVec attrs) tupVec)
166166
167167
168168tupleAsHTML :: DataFrameTuple -> T.Text
@@ -174,7 +174,7 @@ tupleAsHTML tuple = "<tr>" <> T.concat (L.map tupleFrag (tupleAssocs tuple)) <>
174174 atomAsHTML atom = atomToText atom
175175
176176attributesAsHTML :: Attributes -> [AttributeOrder] -> T.Text
177−attributesAsHTML attrs orders' = "<tr>" <> T.concat (map oneAttrHTML (V.toList attrs)) <> "</tr>"
177+attributesAsHTML attrs orders' = "<tr>" <> T.concat (map oneAttrHTML (A.toList attrs)) <> "</tr>"
178178 where
179179 oneAttrHTML attr = "<th>" <> prettyAttribute attr <> ordering (attributeName attr) <> "</th>"
180180 ordering attrName = " " <> case L.find (\(AttributeOrder nam _) -> nam == attrName) orders' of
src/lib/ProjectM36/Relation.hs+2−2
@@ -44,7 +44,7 @@ emptyRelationWithAttrs attrs = Relation attrs emptyTupleSet
4444mkRelation :: Attributes -> RelationTupleSet -> Either RelationalError Relation
4545mkRelation attrs tupleSet =
4646 --check that all attributes are unique- this cannot be done when creating attributes because the check can become expensive
47− let duplicateAttrNames = dupes (sort (map A.attributeName (V.toList attrs))) in
47+ let duplicateAttrNames = dupes (sort (map A.attributeName (V.toList (attributesVec attrs)))) in
4848 if not (null duplicateAttrNames) then
4949 Left (DuplicateAttributeNamesError (S.fromList duplicateAttrNames))
5050 else
@@ -154,7 +154,7 @@ group groupAttrNames newAttrName rel = do
154154 groupProjectionAttributes <- A.projectionAttributesForNames groupAttrNames (attributes rel)
155155 let groupAttr = Attribute newAttrName (RelationAtomType groupProjectionAttributes)
156156 matchingRelTuple tupIn = case imageRelationFor tupIn rel of
157− Right rel2 -> RelationTuple (V.singleton groupAttr) (V.singleton (RelationAtom rel2))
157+ Right rel2 -> RelationTuple (A.singleton groupAttr) (V.singleton (RelationAtom rel2))
158158 Left _ -> undefined
159159 mogrifier tupIn = pure (tupleExtend tupIn (matchingRelTuple tupIn))
160160 newAttrs = A.addAttribute groupAttr nonGroupProjectionAttributes
src/lib/ProjectM36/Relation/Parse/CSV.hs+2−3
@@ -38,8 +38,7 @@ csvAsRelation attrs tConsMap inString = case APBL.parse (csvWithHeader csvDecode
3838 let strHeader = V.map decodeUtf8 headerRaw
3939 strMapRecords = V.map convertMap vecMapsRaw
4040 convertMap hmap = HM.fromList $ L.map (decodeUtf8 *** (T.unpack . decodeUtf8)) (HM.toList hmap)
41− attrNames = V.map A.attributeName attrs
42− attrNameSet = S.fromList (V.toList attrNames)
41+ attrNameSet = A.attributeNameSet attrs
4342 headerSet = S.fromList (V.toList strHeader)
4443 parseAtom attrName aType textIn = case APT.parseOnly (parseCSVAtomP attrName tConsMap aType <* APT.endOfInput) textIn of
4544 Left err -> Left (ParseError (T.pack err))
@@ -47,7 +46,7 @@ csvAsRelation attrs tConsMap inString = case APBL.parse (csvWithHeader csvDecode
4746 makeTupleList :: HM.HashMap AttributeName String -> [Either CsvImportError Atom]
4847 makeTupleList tupMap = V.toList $ V.map (\attr ->
4948 either (Left . AttributeMappingError) Right $
50− parseAtom (A.attributeName attr) (A.atomType attr) (T.pack $ tupMap HM.! A.attributeName attr)) attrs
49+ parseAtom (A.attributeName attr) (A.atomType attr) (T.pack $ tupMap HM.! A.attributeName attr)) (attributesVec attrs)
5150 if attrNameSet == headerSet then do
5251 tupleList <- mapM sequence $ V.toList (V.map makeTupleList strMapRecords)
5352 case mkRelationFromList attrs tupleList of
src/lib/ProjectM36/Relation/Show/CSV.hs+5−5
@@ -1,7 +1,7 @@
11{-# OPTIONS_GHC -fno-warn-orphans #-}
22module ProjectM36.Relation.Show.CSV where
33import ProjectM36.Base
4−import ProjectM36.Attribute
4+import ProjectM36.Attribute as A
55import Data.Csv
66import ProjectM36.Tuple
77import qualified Data.ByteString.Lazy as BS
@@ -17,13 +17,13 @@ relationAsCSV (Relation attrs tupleSet)
1717 | relValAttrs /= [] =
1818 Left $ RelationValuedAttributesNotSupportedError (map attributeName relValAttrs)
1919 --check that there is at least one attribute
20− | V.null attrs =
20+ | A.null attrs =
2121 Left $ TupleAttributeCountMismatchError 0
2222 | otherwise =
2323 Right $ encodeByName bsAttrNames $ map RecordRelationTuple (asList tupleSet)
2424 where
25− relValAttrs = V.toList $ V.filter (isRelationAtomType . atomType) attrs
26− bsAttrNames = V.map (TE.encodeUtf8 . attributeName) attrs
25+ relValAttrs = V.toList $ V.filter (isRelationAtomType . atomType) (attributesVec attrs)
26+ bsAttrNames = V.map (TE.encodeUtf8 . attributeName) (attributesVec attrs)
2727
2828{-
2929instance ToRecord RelationTuple where
@@ -36,7 +36,7 @@ instance ToNamedRecord RecordRelationTuple where
3636 toNamedRecord rTuple = namedRecord $ map (\(k,v) -> TE.encodeUtf8 k .= RecordAtom v) (tupleAssocs $ unTuple rTuple)
3737
3838instance DefaultOrdered RecordRelationTuple where
39− headerOrder (RecordRelationTuple tuple) = V.map (TE.encodeUtf8 . attributeName) (tupleAttributes tuple)
39+ headerOrder (RecordRelationTuple tuple) = V.map (TE.encodeUtf8 . attributeName) (attributesVec (tupleAttributes tuple))
4040
4141newtype RecordAtom = RecordAtom {unAtom :: Atom}
4242
src/lib/ProjectM36/Relation/Show/HTML.hs+2−2
@@ -3,6 +3,7 @@ import ProjectM36.Base
33import ProjectM36.Relation
44import ProjectM36.Tuple
55import ProjectM36.Atom
6+import ProjectM36.Attribute as A
67import ProjectM36.AtomType
78import qualified Data.List as L
89import Data.Text (Text, pack)
@@ -11,10 +12,9 @@ import qualified Data.Text.IO as TIO
1112#if __GLASGOW_HASKELL__ < 804
1213import Data.Monoid
1314#endif
14−import qualified Data.Vector as V
1515
1616attributesAsHTML :: Attributes -> Text
17−attributesAsHTML attrs = "<tr>" <> T.concat (map oneAttrHTML (V.toList attrs)) <> "</tr>"
17+attributesAsHTML attrs = "<tr>" <> T.concat (map oneAttrHTML (A.toList attrs)) <> "</tr>"
1818 where
1919 oneAttrHTML attr = "<th>" <> prettyAttribute attr <> "</th>"
2020
src/lib/ProjectM36/RelationalExpression.hs+9−6
@@ -46,6 +46,8 @@ import Control.Exception
4646import GHC.Paths
4747#endif
4848
49+import Debug.Trace
50+
4951data DatabaseContextExprDetails = CountUpdatedTuples
5052
5153databaseContextExprDetailsFunc :: DatabaseContextExprDetails -> ResultAccumFunc
@@ -788,7 +790,7 @@ updateTupleWithAtomExprs exprMap context graph tupIn = do
788790-}
789791--run verification on all constraints
790792checkConstraints :: DatabaseContext -> TransactionId -> TransactionGraph -> Either RelationalError ()
791−checkConstraints context transId graph@(TransactionGraph graphHeads transSet) =
793+checkConstraints context transId graph@(TransactionGraph graphHeads transSet) = do
792794 mapM_ (uncurry checkIncDep) (M.toList deps)
793795 where
794796 potentialGraph = TransactionGraph graphHeads (S.insert tempTrans transSet)
@@ -804,6 +806,7 @@ checkConstraints context transId graph@(TransactionGraph graphHeads transSet) =
804806 deps = inclusionDependencies context
805807 -- no optimization available here, really? perhaps the optimizer should be passed down to here or the eval function should be passed through the environment
806808 checkIncDep depName (InclusionDependency subsetExpr supersetExpr) = do
809+ traceShowM ("incdep", subsetExpr, supersetExpr)
807810 let process = runProcessExprM UncommittedContextMarker
808811 gfSubsetExpr = process (processRelationalExpr subsetExpr)
809812 gfSupersetExpr = process (processRelationalExpr supersetExpr)
@@ -814,11 +817,11 @@ checkConstraints context transId graph@(TransactionGraph graphHeads transSet) =
814817 typeSuper <- runGfRel (typeForGraphRefRelationalExpr gfSupersetExpr)
815818 when (typeSub /= typeSuper) (Left (RelationTypeMismatchError (attributes typeSub) (attributes typeSuper)))
816819 let checkExpr = Equals gfSupersetExpr (Union gfSubsetExpr gfSupersetExpr)
817− gfEvald = runGraphRefRelationalExprM gfEnv' (evalGraphRefRelationalExpr checkExpr)
820+ gfEvald = runGraphRefRelationalExprM gfEnv' (evalGraphRefRelationalExpr (traceShow ("checkCon", checkExpr) checkExpr))
818821 gfEnv' = freshGraphRefRelationalExprEnv (Just context) potentialGraph
819822 case gfEvald of
820823 Left err -> Left err
821− Right resultRel -> if resultRel == relationTrue then
824+ Right resultRel -> if traceShow ("result", resultRel, relationTrue) (resultRel == relationTrue) then
822825 pure ()
823826 else
824827 Left (InclusionDependencyCheckError depName)
@@ -1165,7 +1168,7 @@ evalGraphRefTupleExprs mAttrs (TupleExprs fixedMarker tupleExprL) = do
11651168 lift $ except $ resolveAttributes accAttr tupAttr
11661169 mostResolvedTypes <-
11671170 foldM (\acc tup -> do
1168− let zipped = zip (V.toList $ tupleAttributes tup) acc
1171+ let zipped = zip (V.toList . attributesVec $ tupleAttributes tup) acc
11691172 accNames = S.fromList $ map A.attributeName acc
11701173 tupNames = A.attributeNameSet (tupleAttributes tup)
11711174 attrNamesDiff = S.union (S.difference accNames tupNames) (S.difference tupNames accNames)
@@ -1176,7 +1179,7 @@ evalGraphRefTupleExprs mAttrs (TupleExprs fixedMarker tupleExprL) = do
11761179 pure nextTupleAttrs
11771180 else
11781181 throwError (TupleAttributeTypeMismatchError diff)
1179− ) (V.toList $ tupleAttributes headTuple) tailTuples
1182+ ) (V.toList . attributesVec $ tupleAttributes headTuple) tailTuples
11801183 pure (A.attributesFromList mostResolvedTypes)
11811184 --strategy: if all the tuple expr transaction markers refer to one location, then we can pass the type constructor mapping from that location, otherwise, we cannot assume that the types are the same
11821185 tConsMap <- case singularTransactions tupleExprL of
@@ -1500,7 +1503,7 @@ relationVariablesAsRelation ctx graph = do
15001503 let gfEnv = freshGraphRefRelationalExprEnv (Just ctx) graph
15011504 gfType <- runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr gfExpr)
15021505 pure (rvName, gfType)
1503− relVarToAtomList (rvName, rel) = [TextAtom rvName, attributesToRel (attributes rel)]
1506+ relVarToAtomList (rvName, rel) = [TextAtom rvName, attributesToRel (attributesVec (attributes rel))]
15041507 attrAtoms a = [TextAtom (A.attributeName a), TextAtom (prettyAtomType (A.atomType a))]
15051508 attributesToRel attrl = case mkRelationFromList subrelAttrs (map attrAtoms (V.toList attrl)) of
15061509 Left err -> error ("relationVariablesAsRelation pooped " ++ show err)
src/lib/ProjectM36/Serialise/Base.hs+11−1
@@ -13,6 +13,7 @@ import Data.Word
1313import qualified Data.List.NonEmpty as NE
1414import qualified Data.Vector as V
1515import Data.Time.Calendar (Day,toGregorian,fromGregorian)
16+import qualified Data.HashSet as HS
1617
1718deriving via WineryVariant Atom instance Serialise Atom
1819deriving via WineryVariant AtomType instance Serialise AtomType
@@ -72,5 +73,14 @@ instance Serialise Day where
7273 extractor = fromGregorianTup <$> extractor
7374 decodeCurrent = fromGregorianTup <$> decodeCurrent
7475
76+instance Serialise Attributes where
77+ schemaGen _ = SVector <$> getSchema (Proxy @Attribute)
78+ toBuilder attrs = varInt (V.length (attributesVec attrs)) <> foldMap toBuilder (V.toList (attributesVec attrs))
79+ extractor =
80+ let mkAttrs x = Attributes x (HS.fromList (V.toList x)) in
81+ mkAttrs <$> extractListBy extractor
7582
76−
83+ decodeCurrent = do
84+ n <- decodeVarInt
85+ l <- replicateM n decodeCurrent
86+ pure (Attributes (V.fromList l) (HS.fromList l))
src/lib/ProjectM36/Tuple.hs+31−28
@@ -1,7 +1,7 @@
11module ProjectM36.Tuple where
22import ProjectM36.Base
33import ProjectM36.Error
4−import ProjectM36.Attribute
4+import ProjectM36.Attribute as A
55import ProjectM36.Atom
66import ProjectM36.AtomType
77import ProjectM36.DataTypes.Primitive
@@ -14,20 +14,22 @@ import Control.Monad
1414import Control.Arrow
1515import Data.Maybe
1616
17+import Debug.Trace
18+
1719emptyTuple :: RelationTuple
18−emptyTuple = RelationTuple V.empty V.empty
20+emptyTuple = RelationTuple mempty mempty
1921
2022tupleSize :: RelationTuple -> Int
21−tupleSize (RelationTuple tupAttrs _) = V.length tupAttrs
23+tupleSize (RelationTuple tupAttrs _) = arity tupAttrs
2224
2325tupleAttributeNameSet :: RelationTuple -> S.Set AttributeName
24−tupleAttributeNameSet (RelationTuple tupAttrs _) = S.fromList $ V.toList $ V.map attributeName tupAttrs
26+tupleAttributeNameSet (RelationTuple tupAttrs _) = attributeNameSet tupAttrs
2527
2628tupleAttributes :: RelationTuple -> Attributes
2729tupleAttributes (RelationTuple tupAttrs _) = tupAttrs
2830
2931tupleAssocs :: RelationTuple -> [(AttributeName, Atom)]
30−tupleAssocs (RelationTuple attrVec tupVec) = V.toList $ V.map (first attributeName) (V.zip attrVec tupVec)
32+tupleAssocs (RelationTuple attrs tupVec) = V.toList $ V.map (first attributeName) (V.zip (attributesVec attrs) tupVec)
3133
3234orderedTupleAssocs :: RelationTuple -> [(AttributeName, Atom)]
3335orderedTupleAssocs tup@(RelationTuple attrVec _) = map (\attr -> (attributeName attr, atomForAttr (attributeName attr))) oAttrs
@@ -42,7 +44,7 @@ tupleAtoms :: RelationTuple -> V.Vector Atom
4244tupleAtoms (RelationTuple _ tupVec) = tupVec
4345
4446atomForAttributeName :: AttributeName -> RelationTuple -> Either RelationalError Atom
45−atomForAttributeName attrName (RelationTuple tupAttrs tupVec) = case V.findIndex (\attr -> attributeName attr == attrName) tupAttrs of
47+atomForAttributeName attrName (RelationTuple tupAttrs tupVec) = case V.findIndex (\attr -> attributeName attr == attrName) (attributesVec tupAttrs) of
4648 Nothing -> Left (NoSuchAttributeNamesError (S.singleton attrName))
4749 Just index -> case tupVec V.!? index of
4850 Nothing -> Left (NoSuchAttributeNamesError (S.singleton attrName))
@@ -59,7 +61,7 @@ vectorIndicesForAttributeNames attrNameVec attrs = if not $ V.null unknownAttrNa
5961 Right $ V.map mapper attrNameVec
6062 where
6163 unknownAttrNames = V.filter (`V.notElem` attributeNames attrs) attrNameVec
62− mapper attrName = fromMaybe (error "logic failure in vectorIndicesForAttributeNames") (V.elemIndex attrName (V.map attributeName attrs))
64+ mapper attrName = fromMaybe (error "logic failure in vectorIndicesForAttributeNames") (V.elemIndex attrName (attributeNames attrs))
6365
6466
6567relationForAttributeName :: AttributeName -> RelationTuple -> Either RelationalError Relation
@@ -86,10 +88,10 @@ mkRelationTuples attrs = map mapper
8688 mapper = mkRelationTuple attrs
8789
8890mkRelationTupleFromMap :: M.Map AttributeName Atom -> RelationTuple
89−mkRelationTupleFromMap attrMap = RelationTuple attrs (V.map (attrMap M.!) attrNames)
91+mkRelationTupleFromMap attrMap = RelationTuple (attributesFromList attrs) (V.map (attrMap M.!) (V.fromList attrNames))
9092 where
91− attrNames = V.fromList (M.keys attrMap)
92− attrs = V.map (\attrName -> Attribute attrName (atomTypeForAtom (attrMap M.! attrName))) attrNames
93+ attrNames = M.keys attrMap
94+ attrs = map (\attrName -> Attribute attrName (atomTypeForAtom (attrMap M.! attrName))) attrNames
9395
9496--return error if attribute names match but their types do not
9597singleTupleSetJoin :: Attributes -> RelationTuple -> RelationTupleSet -> Either RelationalError [RelationTuple]
@@ -120,8 +122,8 @@ singleTupleJoin joinedAttrs tup1@(RelationTuple tupAttrs1 _) tup2@(RelationTuple
120122 return $ Just $ RelationTuple joinedAttrs newVec
121123 where
122124 keysIntersection = V.map attributeName attrsIntersection
123− attrsIntersection = V.filter (`V.elem` tupAttrs1) tupAttrs2
124− newVec = V.map (findAtomForAttributeName . attributeName) joinedAttrs
125+ attrsIntersection = V.filter (`V.elem` attributesVec tupAttrs1) (attributesVec tupAttrs2)
126+ newVec = V.map (findAtomForAttributeName . attributeName) (attributesVec joinedAttrs)
125127 --search both tuples for the attribute
126128 findAtomForAttributeName attrName = head $ rights $ fmap (atomForAttributeName attrName) [tup1, tup2]
127129
@@ -138,21 +140,21 @@ vectorUnion v1 v2 = V.foldr folder v1 v2
138140tupleExtend :: RelationTuple -> RelationTuple -> RelationTuple
139141tupleExtend (RelationTuple tupAttrs1 tupVec1) (RelationTuple tupAttrs2 tupVec2) = RelationTuple newAttrs newVec
140142 where
141− newAttrs = tupAttrs1 V.++ tupAttrs2
142− newVec = tupVec1 V.++ tupVec2
143+ newAttrs = tupAttrs1 <> tupAttrs2
144+ newVec = tupVec1 <> tupVec2
143145
144146tupleAtomExtend :: AttributeName -> Atom -> RelationTuple -> RelationTuple
145147tupleAtomExtend newAttrName atom tupIn = tupleExtend tupIn newTup
146148 where
147− newTup = RelationTuple (V.singleton $ Attribute newAttrName (atomTypeForAtom atom)) (V.singleton atom)
149+ newTup = RelationTuple (A.singleton $ Attribute newAttrName (atomTypeForAtom atom)) (V.singleton atom)
148150
149151--this could be cheaper- it may not be wortwhile to update all the tuples for projection, but then the attribute management must be slightly different- perhaps the attributes should be a vector of association tuples [(name, index)]
150152tupleProject :: S.Set AttributeName -> RelationTuple -> RelationTuple
151153tupleProject projectAttrs (RelationTuple attrs tupVec) = RelationTuple newAttrs newTupVec
152154 where
153− deleteIndices = V.findIndices (\attr -> S.notMember (attributeName attr) projectAttrs) attrs
155+ deleteIndices = V.findIndices (\attr -> S.notMember (attributeName attr) projectAttrs) (attributesVec attrs)
154156 indexDeleter = V.ifilter (\index _ -> V.notElem index deleteIndices)
155− newAttrs = indexDeleter attrs
157+ newAttrs = deleteAttributeNames projectAttrs attrs
156158 newTupVec = indexDeleter tupVec
157159
158160--return the attributes and atoms which are equal in both vectors
@@ -162,12 +164,10 @@ tupleIntersection tuple1 tuple2 = RelationTuple newAttrs newTupVec
162164 where
163165 attrs1 = tupleAttributes tuple1
164166 attrs2 = tupleAttributes tuple2
165− matchingIndices = V.findIndices (\attr -> V.elem attr attrs2 &&
166− atomForAttributeName (attributeName attr) tuple1 ==
167− atomForAttributeName (attributeName attr) tuple2
168− ) attrs1
167+ intersectAttrs = A.intersection attrs1 attrs2
168+ matchingIndices = V.findIndices (\attr -> A.member attr intersectAttrs) (attributesVec attrs1)
169169 indexFilter = V.ifilter (\index _ -> V.elem index matchingIndices)
170− newAttrs = indexFilter attrs1
170+ newAttrs = deleteAttributeNames (A.attributeNameSet intersectAttrs) attrs1
171171 newTupVec = indexFilter (tupleAtoms tuple1)
172172
173173-- | An optimized form of tuple update which updates vectors efficiently.
@@ -175,19 +175,19 @@ updateTupleWithAtoms :: M.Map AttributeName Atom -> RelationTuple -> RelationTup
175175updateTupleWithAtoms updateMap (RelationTuple attrs tupVec) = RelationTuple attrs newVec
176176 where
177177 updateKeysSet = M.keysSet updateMap
178− updateKeysIVec = V.filter (\(_,attr) -> S.member (attributeName attr) updateKeysSet) (V.indexed attrs)
178+ updateKeysIVec = V.filter (\(_,attr) -> S.member (attributeName attr) updateKeysSet) (V.indexed (attributesVec attrs))
179179 newVec = V.update tupVec updateVec
180180 updateVec = V.map (\(index, attr) -> (index, updateMap M.! attributeName attr)) updateKeysIVec
181181
182182tupleToMap :: RelationTuple -> M.Map AttributeName Atom
183183tupleToMap (RelationTuple attrs tupVec) = M.fromList assocList
184184 where
185− assocList = V.toList $ V.map (\(index, attr) -> (attributeName attr, tupVec V.! index)) (V.indexed attrs)
185+ assocList = V.toList $ V.map (\(index, attr) -> (attributeName attr, tupVec V.! index)) (V.indexed (attributesVec attrs))
186186
187187verifyTuple :: Attributes -> RelationTuple -> Either RelationalError RelationTuple
188−verifyTuple attrs tuple = let attrsTypes = V.map atomType attrs
188+verifyTuple attrs tuple = let attrsTypes = A.atomTypes attrs
189189 tupleTypes = V.map atomTypeForAtom (tupleAtoms tuple) in
190− if V.length attrs /= V.length tupleTypes then
190+ if A.arity attrs /= V.length tupleTypes then
191191 Left $ TupleAttributeCountMismatchError 0
192192 else do
193193 mapM_ (uncurry atomTypeVerify) (V.zip attrsTypes tupleTypes)
@@ -199,7 +199,7 @@ reorderTuple :: Attributes -> RelationTuple -> RelationTuple
199199reorderTuple attrs tupIn = if tupleAttributes tupIn == attrs then
200200 tupIn
201201 else
202− RelationTuple attrs (V.map mapper attrs)
202+ RelationTuple attrs (V.map mapper (attributesVec attrs))
203203 where
204204 mapper attr = case atomForAttributeName (attributeName attr) tupIn of
205205 Left err -> error ("logic bug in reorderTuple: " ++ show err)
@@ -207,4 +207,7 @@ reorderTuple attrs tupIn = if tupleAttributes tupIn == attrs then
207207
208208--used in Generics derivation for ADTs without named attributes
209209trimTuple :: Int -> RelationTuple -> RelationTuple
210−trimTuple index (RelationTuple attrs vals) = RelationTuple (V.drop index attrs) (V.drop index vals)
210+trimTuple index (RelationTuple attrs vals) =
211+ RelationTuple newAttrs (V.drop index vals)
212+ where
213+ newAttrs = A.drop index attrs
src/lib/ProjectM36/Tupleable.hs+10−8
@@ -38,13 +38,14 @@ import qualified Data.Text as T
3838import qualified Data.Vector as V
3939import GHC.Generics
4040import ProjectM36.Atomable
41−import ProjectM36.Attribute hiding (null)
41+import ProjectM36.Attribute as A hiding (null, toList)
4242import ProjectM36.Base
4343import ProjectM36.DataTypes.Primitive
4444import ProjectM36.Error
4545import ProjectM36.Tuple
4646import ProjectM36.TupleSet
4747import qualified Data.Set as S
48+import qualified Data.HashSet as HS
4849
4950{-import Data.Binary
5051import Control.DeepSeq
@@ -89,7 +90,7 @@ toInsertExpr vals rvName = do
8990
9091-- | Convert a 'Tupleable' to a create a 'Define' expression which can be used to create an empty relation variable. Use 'toInsertExpr' to insert the actual tuple data. This function is typically used with 'Data.Proxy'.
9192toDefineExpr :: forall a proxy. Tupleable a => proxy a -> RelVarName -> DatabaseContextExpr
92−toDefineExpr _ rvName = Define rvName (map NakedAttributeExpr (V.toList attrs))
93+toDefineExpr _ rvName = Define rvName (map NakedAttributeExpr (V.toList (attributesVec attrs)))
9394 where
9495 attrs = toAttributes (Proxy :: Proxy a)
9596
@@ -228,11 +229,12 @@ instance (Constructor c, TupleableG a, AtomableG a) => TupleableG (M1 C c a) whe
228229 toTupleG opts (M1 v) = RelationTuple attrs atoms
229230 where
230231 attrsToCheck = toAttributesG opts v
231− counter = V.generate (V.length attrsToCheck) id
232− attrs = V.zipWith (\num attr@(Attribute name typ) -> if T.null name then
232+ counter = V.generate (arity attrsToCheck) id
233+ attrs = Attributes attrsV (HS.fromList (V.toList attrsV))
234+ attrsV = V.zipWith (\num attr@(Attribute name typ) -> if T.null name then
233235 Attribute ("attr" <> T.pack (show (num + 1))) typ
234236 else
235− attr) counter attrsToCheck
237+ attr) counter (attributesVec attrsToCheck)
236238 atoms = V.fromList (toAtomsG v)
237239 toAttributesG opts (M1 v) = toAttributesG opts v
238240 fromTupleG opts tup = M1 <$> fromTupleG opts tup
@@ -241,7 +243,7 @@ instance (Constructor c, TupleableG a, AtomableG a) => TupleableG (M1 C c a) whe
241243-- product types
242244instance (TupleableG a, TupleableG b) => TupleableG (a :*: b) where
243245 toTupleG = error "toTupleG"
244− toAttributesG opts ~(x :*: y) = toAttributesG opts x V.++ toAttributesG opts y --a bit of extra laziness prevents whnf so that we can use toAttributes (undefined :: Test2T Int Int) without throwing an exception
246+ toAttributesG opts ~(x :*: y) = toAttributesG opts x <> toAttributesG opts y --a bit of extra laziness prevents whnf so that we can use toAttributes (undefined :: Test2T Int Int) without throwing an exception
245247 fromTupleG opts tup = (:*:) <$> fromTupleG opts tup <*> fromTupleG opts processedTuple
246248 where
247249 processedTuple = if isRecordTypeG (undefined :: a x) then
@@ -253,7 +255,7 @@ instance (TupleableG a, TupleableG b) => TupleableG (a :*: b) where
253255--selector/record
254256instance (Selector c, AtomableG a) => TupleableG (M1 S c a) where
255257 toTupleG = error "toTupleG"
256− toAttributesG opts m@(M1 v) = V.singleton (Attribute modifiedName aType)
258+ toAttributesG opts m@(M1 v) = A.singleton (Attribute modifiedName aType)
257259 where
258260 name = T.pack (selName m)
259261 modifiedName = if T.null name then
@@ -268,7 +270,7 @@ instance (Selector c, AtomableG a) => TupleableG (M1 S c a) where
268270 val <- atomv atom
269271 pure (M1 val)
270272 where
271− expectedAtomType = atomType (V.head (toAttributesG opts (undefined :: M1 S c a x)))
273+ expectedAtomType = atomType (V.head (attributesVec (toAttributesG opts (undefined :: M1 S c a x))))
272274 atomv atom = maybe (Left (AtomTypeMismatchError
273275 expectedAtomType
274276 (atomTypeForAtom atom)
test/Client/Simple.hs+1−2
@@ -9,7 +9,6 @@ import System.IO.Temp
99import System.FilePath
1010import ProjectM36.TupleSet
1111import ProjectM36.Attribute
12−import qualified Data.Vector as V
1312import qualified Data.Map as M
1413
1514main :: IO ()
@@ -59,7 +58,7 @@ testSimpleCommitFailure = TestCase $ do
5958 execute $ Assign "x" (ExistingRelation relationTrue)
6059 --cause error
6160 execute $ Assign "x" (MakeStaticRelation failAttrs emptyTupleSet)
62− let expectedErr = Left (RelError (RelationTypeMismatchError V.empty failAttrs))
61+ let expectedErr = Left (RelError (RelationTypeMismatchError mempty failAttrs))
6362 assertEqual "dbc error" expectedErr err
6463
6564-- #176 default merge couldn't handle Update
test/Relation/Basic.hs+1−1
@@ -65,7 +65,7 @@ validateAttrTypesMatchTupleAttrTypes rel@(Relation attrs tupSet) = foldr (\tuple
6565 attrChecks tuple = V.map (\attr -> case atomForAttributeName (A.attributeName attr) tuple of
6666 Left _ -> False
6767 Right atom -> Right (atomTypeForAtom atom) ==
68− A.atomTypeForAttributeName (A.attributeName attr) attrs) (attributes rel)
68+ A.atomTypeForAttributeName (A.attributeName attr) attrs) (attributesVec (attributes rel))
6969
7070simpleRel :: Relation
7171simpleRel = case mkRelation attrs tupleSet of
test/Relation/Tupleable.hs+5−4
@@ -13,7 +13,7 @@
1313import Test.HUnit
1414import ProjectM36.Tupleable.Deriving
1515import ProjectM36.Atomable
16−import ProjectM36.Attribute
16+import ProjectM36.Attribute as A
1717import ProjectM36.Error
1818import ProjectM36.Base
1919import Control.DeepSeq (NFData)
@@ -110,7 +110,7 @@ main = do
110110 if errors tcounts + failures tcounts > 0 then exitFailure else exitSuccess
111111
112112testList :: Test
113−testList = TestList [testADT1, testADT2, testADT3, testADT4, testADT6, testADT7, testADT8, testInsertExpr, testDefineExpr, testUpdateExpr, testUpdateExprEmptyAttrs, testDeleteExpr, testUpdateExprWrongAttr, testReorderedTuple, testAddPrefixField, testDropPrefixField, testAddSuffixField, testDropSuffixField, testUpperCaseField, testLowerCaseField, testTitleCaseField, testCamelCaseField, testPascalCaseField, testSnakeCaseField, testSpinalCaseField, testTrainCaseField, testAsIsCodec, testAsIsField, testComposeRLCodec, testComposeRLField, testComposeLRCodec, testComposeLRField]
113+testList = TestList [testADT1, testADT2, testADT3, testADT4, testADT6, testADT7, testADT8 ,testInsertExpr, testDefineExpr, testUpdateExpr, testUpdateExprEmptyAttrs, testDeleteExpr, testUpdateExprWrongAttr, testReorderedTuple, testAddPrefixField, testDropPrefixField, testAddSuffixField, testDropSuffixField, testUpperCaseField, testLowerCaseField, testTitleCaseField, testCamelCaseField, testPascalCaseField, testSnakeCaseField, testSpinalCaseField, testTrainCaseField, testAsIsCodec, testAsIsField, testComposeRLCodec, testComposeRLField, testComposeLRCodec, testComposeLRField]
114114
115115testADT1 :: Test
116116testADT1 = TestCase $ assertEqual "one record constructor" (Right example) (fromTuple (toTuple example))
@@ -148,7 +148,7 @@ testADT7 :: Test
148148testADT7 = TestCase $ do
149149 let example = Test7C (Test7AC 3)
150150 assertEqual "atom type" (Right example) (fromTuple (toTuple example))
151− let expectedAttrs = V.singleton (Attribute "" (ConstructedAtomType "Test7A" M.empty))
151+ let expectedAttrs = A.singleton (Attribute "" (ConstructedAtomType "Test7A" M.empty))
152152 assertEqual "adt atomtype" expectedAttrs (toAttributes (Proxy :: Proxy Test7T))
153153
154154testADT8 :: Test
@@ -222,7 +222,8 @@ testDeleteExpr = TestCase $ do
222222testReorderedTuple :: Test
223223testReorderedTuple = TestCase $ do
224224 let tupleRev :: RelationTuple -> RelationTuple
225− tupleRev (RelationTuple v1 v2) = RelationTuple (V.reverse v1) (V.reverse v2)
225+ tupleRev (RelationTuple v1 v2) = RelationTuple (revAttrs v1) (V.reverse v2)
226+ revAttrs attrs = A.attributesFromList (reverse (A.toList attrs))
226227 expected = Test2C {attrB = 3,
227228 attrC = 4}
228229 actual = fromTuple . tupleRev . toTuple $ expected