From 6cd3b92ad17765706b38ab73573f590b128d1277 Mon Sep 17 00:00:00 2001 From: AgentM Date: Sun, 6 Aug 2023 19:17:52 -0400 Subject: [PATCH] WIP join checkpoint tests pass enable bulk renaming in Rename expression to make expression equality easier to find --- project-m36.cabal | 1 - src/bin/SQL/Interpreter/Convert.hs | 141 ++++++++++++------ src/bin/SQL/Interpreter/Select.hs | 9 +- .../TutorialD/Interpreter/RelationalExpr.hs | 4 +- src/bin/TutorialD/Printer.hs | 7 +- src/lib/ProjectM36/AtomFunctions/Primitive.hs | 2 +- src/lib/ProjectM36/Base.hs | 2 +- src/lib/ProjectM36/HashSecurely.hs | 9 +- src/lib/ProjectM36/IsomorphicSchema.hs | 2 +- src/lib/ProjectM36/Key.hs | 2 +- src/lib/ProjectM36/NormalizeExpr.hs | 4 +- src/lib/ProjectM36/Relation.hs | 5 + src/lib/ProjectM36/RelationalExpression.hs | 14 +- src/lib/ProjectM36/Shortcuts.hs | 3 +- src/lib/ProjectM36/StaticOptimizer.hs | 8 +- .../TransGraphRelationalExpression.hs | 4 +- src/lib/ProjectM36/WithNameExpr.hs | 4 +- test/SQL/InterpreterTest.hs | 9 +- 18 files changed, 148 insertions(+), 82 deletions(-) diff --git a/project-m36.cabal b/project-m36.cabal index 2eca4b61..461d6f07 100644 --- a/project-m36.cabal +++ b/project-m36.cabal @@ -214,7 +214,6 @@ Executable tutd TutorialD.Interpreter.Types, TutorialD.Interpreter.TransGraphRelationalOperator, TutorialD.Interpreter.SchemaOperator, - TutorialD.Printer, SQL.Interpreter.Base, SQL.Interpreter.Select, SQL.Interpreter.Convert diff --git a/src/bin/SQL/Interpreter/Convert.hs b/src/bin/SQL/Interpreter/Convert.hs index c77a07cd..2a193b26 100644 --- a/src/bin/SQL/Interpreter/Convert.hs +++ b/src/bin/SQL/Interpreter/Convert.hs @@ -12,14 +12,16 @@ import Data.Text as T (pack,intercalate,Text,concat) import ProjectM36.Relation import Control.Monad (foldM) import qualified Data.Set as S +import qualified Data.Map as M import Data.List (foldl') import qualified Data.Functor.Foldable as Fold import Debug.Trace data SQLError = NotSupportedError T.Text | - TypeMismatch AtomType AtomType | - NoSuchFunction QualifiedName | + TypeMismatchError AtomType AtomType | + NoSuchSQLFunctionError QualifiedName | + DuplicateTableReferenceError QualifiedName | SQLRelationalError RelationalError deriving (Show, Eq) @@ -99,16 +101,24 @@ instance SQLConvert [SelectItem] where -- apply extensions let fExtended = foldl' (\acc ext -> (Extend ext) . acc) id (taskExtenders task) -- apply rename - fRenames <- foldM (\acc (qProjName, (AliasName newName)) -> do + renamesSet <- foldM (\acc (qProjName, (AliasName newName)) -> do oldName <- convert typeF qProjName - pure $ Rename oldName newName . acc) id (taskRenames task) + pure $ S.insert (oldName, newName) acc) S.empty (taskRenames task) + let fRenames = if S.null renamesSet then id else Rename renamesSet pure (fExtended . fProjection . fRenames) instance SQLConvert TableExpr where type ConverterF TableExpr = (RelationalExpr, WithNamesAssocs) --does not handle non-relational aspects such as offset, order by, or limit convert typeF tExpr = do - (fromExpr, withExprs) <- convert typeF (fromClause tExpr) + (fromExpr, tableAliasMap) <- convert typeF (fromClause tExpr) + let tableAliasMap' = M.filterWithKey filterRedundantAlias tableAliasMap + filterRedundantAlias (QualifiedName [nam]) (RelationVariable nam' ()) + | nam == nam' = False + filterRedundantAlias _ _ = True + withExprs <- mapM (\(qnam, expr) -> do + nam <- convert typeF qnam + pure (WithNameExpr nam (), expr)) (M.toList tableAliasMap') expr' <- case whereClause tExpr of Just whereExpr -> do restrictPredExpr <- convert typeF whereExpr @@ -121,29 +131,35 @@ instance SQLConvert TableExpr where instance SQLConvert [TableRef] where -- returns base relation expressions plus top-level renames required - type ConverterF [TableRef] = (RelationalExpr, WithNamesAssocs) - convert _ [] = pure (ExistingRelation relationFalse, []) + type ConverterF [TableRef] = (RelationalExpr, TableAliasMap) + convert _ [] = pure (ExistingRelation relationFalse, M.empty) convert typeF (firstRef:trefs) = do --the first table ref must be a straight RelationVariable - (firstRel, withRenames) <- convert typeF firstRef - (expr', withRenames') <- foldM joinTRef (firstRel, withRenames) (zip [1..] trefs) - pure (expr', withRenames') + (firstRel, tableAliases) <- convert typeF firstRef + (expr', tableAliases') <- foldM joinTRef (firstRel, tableAliases) (zip [1..] trefs) + pure (expr', tableAliases') where --TODO: if any of the previous relations have overlap in their attribute names, we must change it to prevent a natural join! - joinTRef (rvA,withRenames) (c,tref) = do - let renamerFolder x expr old_name = - let new_name = T.concat [old_name, "_", x, T.pack (show c)] - in - pure $ Rename old_name new_name expr + joinTRef (rvA,tAliases) (c,tref) = do + let attrRenamer x expr attrs = do + renamed <- mapM (renameOneAttr x expr) attrs + pure (Rename (S.fromList renamed) expr) + renameOneAttr x expr old_name = pure (old_name, new_name) + where + new_name = T.concat [prefix, ".", old_name] + prefix = case expr of + RelationVariable rvName () -> rvName + _ -> x -- probably need to return errors for some expressions + case tref of NaturalJoinTableRef jtref -> do -- then natural join is the only type of join which the relational algebra supports natively - (rvB, withRenames') <- convert typeF jtref - pure $ (Join rvA rvB, withRenames <> withRenames') + (rvB, tAliases') <- convert typeF jtref + pure $ (Join rvA rvB, M.union tAliases tAliases) CrossJoinTableRef jtref -> do --rename all columns to prefix them with a generated alias to prevent any natural join occurring, then perform normal join -- we need the type to get all the attribute names for both relexprs - (rvB, withRenames') <- convert typeF jtref + (rvB, tAliases) <- convert typeF jtref case typeF rvA of Left err -> Left (SQLRelationalError err) Right typeA -> @@ -154,36 +170,51 @@ instance SQLConvert [TableRef] where attrsB = A.attributeNameSet (attributes typeB) attrsIntersection = S.intersection attrsA attrsB --find intersection of attributes and rename all of them with prefix 'expr'+c+'.' - traceShowM ("cross gonk", attrsIntersection) - exprA <- foldM (renamerFolder "a") rvA (S.toList attrsIntersection) - pure (Join exprA rvB, withRenames') + exprA <- attrRenamer "a" rvA (S.toList attrsIntersection) + pure (Join exprA rvB, tAliases) InnerJoinTableRef jtref (JoinUsing qnames) -> do - (rvB, withRenames') <- convert typeF jtref + (rvB, tAliases) <- convert typeF jtref jCondAttrs <- S.fromList <$> mapM (convert typeF) qnames (attrsIntersection, attrsA, attrsB) <- commonAttributeNames typeF rvA rvB - --rename attributes which are not part of the join condition + --rename attributes used in the join condition let attrsToRename = S.difference attrsIntersection jCondAttrs - traceShowM ("inner", attrsToRename, attrsIntersection, jCondAttrs) - exprA <- foldM (renamerFolder "a") rvA (S.toList attrsToRename) - pure (Join exprA rvB, withRenames') - InnerJoinTableRef jtref (JoinOn sexpr) -> do +-- traceShowM ("inner", attrsToRename, attrsIntersection, jCondAttrs) + exprA <- attrRenamer "a" rvA (S.toList attrsToRename) + pure (Join exprA rvB, tAliases) + + InnerJoinTableRef jtref (JoinOn (JoinOnCondition joinExpr)) -> do --create a cross join but extend with the boolean sexpr --extend the table with the join conditions, then join on those --exception: for simple attribute equality, use regular join renames using JoinOn logic - (rvB, withRenames') <- convert typeF jtref + (rvB, tAliases) <- convert typeF jtref + + --rvA and rvB now reference potentially aliased relation variables (needs with clause to execute), but this is useful for making attributes rv-prefixed +-- traceShowM ("converted", rvA, rvB, tAliases) --extract all table aliases to create a remapping for SQL names discovered in the sexpr - (commonAttrs, attrsA, attrsB) <- commonAttributeNames typeF rvA rvB - let sexpr' = renameIdentifier renamer sexpr + (commonAttrs, attrsA, attrsB) <- commonAttributeNames typeF rvA rvB + -- first, execute the rename, renaming all attributes according to their table aliases + let rvPrefix rvExpr = + case rvExpr of + RelationVariable nam () -> pure nam + x -> Left $ NotSupportedError ("cannot derived name for relational expression " <> T.pack (show x)) + rvPrefixA <- rvPrefix rvA + rvPrefixB <- rvPrefix rvB + exprA <- attrRenamer rvPrefixA rvA (S.toList attrsA) + exprB <- attrRenamer rvPrefixB rvB (S.toList attrsB) + -- for the join condition, we can potentially extend to include all the join criteria columns, then project them away after constructing the join condition + let joinExpr' = renameIdentifier renamer joinExpr renamer n@(QualifiedName [tableAlias,attr]) = --lookup prefixed with table alias - case W.lookup tableAlias withRenames' of - Nothing -> QualifiedName [attr]-- the table was not renamed, but the attribute may have been renamed- how do we know at this point when the sexpr' converter hasn't run yet?! + case M.lookup n tAliases of + -- the table was not renamed, but the attribute may have been renamed + -- find the source of the attribute + Nothing -> n Just found -> error (show (tableAlias, found)) renamer n@(QualifiedName [attr]) = error (show n) - joinRe <- convert typeF sexpr' +-- traceShowM ("joinExpr'", joinExpr') + joinRe <- convert typeF joinExpr' + --let joinCommonAttrRenamer (RelationVariable rvName ()) old_name = --rename all common attrs and use the new names in the join condition - exprA <- foldM (renamerFolder "a") rvA (S.toList commonAttrs) - exprB <- foldM (renamerFolder "b") rvB (S.toList commonAttrs) let allAttrs = S.union attrsA attrsB firstAvailableName c allAttrs' = let new_name = T.pack ("join_" <> show c) in @@ -191,9 +222,14 @@ instance SQLConvert [TableRef] where firstAvailableName (c + 1) allAttrs' else new_name - extender = AttributeExtendTupleExpr (firstAvailableName 1 allAttrs) joinRe - pure (Join (Extend extender exprA) exprB, withRenames') + joinName = firstAvailableName 1 allAttrs + extender = AttributeExtendTupleExpr joinName joinRe + joinMatchRestriction = Restrict (AttributeEqualityPredicate joinName (ConstructedAtomExpr "True" [] ())) + projectAwayJoinMatch = Project (InvertedAttributeNames (S.fromList [joinName])) + pure (projectAwayJoinMatch (joinMatchRestriction (Extend extender (Join exprB exprA))), tAliases) + +--type AttributeNameRemap = M.Map RelVarName AttributeName -- | Used in join condition detection necessary for renames to enable natural joins. commonAttributeNames :: TypeForRelExprF -> RelationalExpr -> RelationalExpr -> Either SQLError (S.Set AttributeName, S.Set AttributeName, S.Set AttributeName) @@ -208,25 +244,34 @@ commonAttributeNames typeF rvA rvB = attrsB = A.attributeNameSet (attributes typeB) pure $ (S.intersection attrsA attrsB, attrsA, attrsB) - +--over the course of conversion, we collect all the table aliases we encounter, including non-aliased table references +type TableAliasMap = M.Map QualifiedName RelationalExpr + +insertTableAlias :: QualifiedName -> RelationalExpr -> TableAliasMap -> Either SQLError TableAliasMap +insertTableAlias qn expr map' = + case M.lookup qn map' of + Nothing -> pure $ M.insert qn expr map' + Just _ -> Left (DuplicateTableReferenceError qn) + -- convert a TableRef in isolation- to be used with the first TableRef only instance SQLConvert TableRef where -- return base relation variable expression plus a function to apply top-level rv renames using WithNameExpr - type ConverterF TableRef = (RelationalExpr, WithNamesAssocs) + type ConverterF TableRef = (RelationalExpr, TableAliasMap) --SELECT x FROM a,_b_ creates a cross join - convert _ (SimpleTableRef (QualifiedName [nam])) = - pure (RelationVariable nam (), []) + convert _ (SimpleTableRef qn@(QualifiedName [nam])) = do + let rv = RelationVariable nam () + pure (rv, M.singleton qn rv) -- include with clause even for simple cases because we use this mapping to convert typeF (AliasedTableRef tnam (AliasName newName)) = do - (rv, withNames) <- convert typeF tnam - pure $ (RelationVariable newName (), (WithNameExpr newName (), rv):withNames) + (rv, _) <- convert typeF tnam + pure $ (RelationVariable newName (), M.singleton (QualifiedName [newName]) rv) convert _ x = Left $ NotSupportedError (T.pack (show x)) instance SQLConvert RestrictionExpr where type ConverterF RestrictionExpr = RestrictionPredicateExpr convert typeF (RestrictionExpr rexpr) = do - let wrongType t = Left $ TypeMismatch t BoolAtomType --must be boolean expression + let wrongType t = Left $ TypeMismatchError t BoolAtomType --must be boolean expression attrName' (QualifiedName ts) = T.intercalate "." ts case rexpr of IntegerLiteral{} -> wrongType IntegerAtomType @@ -247,7 +292,7 @@ lookupFunc qname = case qname of QualifiedName [nam] -> case lookup nam sqlFuncs of - Nothing -> Left $ NoSuchFunction qname + Nothing -> Left $ NoSuchSQLFunctionError qname Just match -> pure match where f n args = FunctionAtomExpr n args () @@ -276,6 +321,12 @@ instance SQLConvert ScalarExpr where f <- lookupFunc qn pure $ f [a,b] +instance SQLConvert JoinOnCondition where + type ConverterF JoinOnCondition = (RelationalExpr -> RelationalExpr) + convert typeF (JoinOnCondition expr) = do + case expr of + Identifier (QualifiedName [tAlias, colName]) -> undefined + instance SQLConvert ProjectionScalarExpr where type ConverterF ProjectionScalarExpr = AtomExpr convert typeF expr = do diff --git a/src/bin/SQL/Interpreter/Select.hs b/src/bin/SQL/Interpreter/Select.hs index 98dd5f96..ae115ffe 100644 --- a/src/bin/SQL/Interpreter/Select.hs +++ b/src/bin/SQL/Interpreter/Select.hs @@ -85,7 +85,10 @@ data NullsOrder = NullsFirst | NullsLast data JoinType = InnerJoin | RightOuterJoin | LeftOuterJoin | FullOuterJoin | CrossJoin | NaturalJoin deriving (Show, Eq) -data JoinCondition = JoinOn ScalarExpr | JoinUsing [UnqualifiedName] +data JoinCondition = JoinOn JoinOnCondition | JoinUsing [UnqualifiedName] + deriving (Show, Eq) + +newtype JoinOnCondition = JoinOnCondition ScalarExpr deriving (Show, Eq) data Alias = Alias QualifiedName (Maybe AliasName) @@ -98,7 +101,7 @@ data ProjectionName = ProjectionName Text | Asterisk deriving (Show, Eq, Ord) data QualifiedName = QualifiedName [Text] - deriving (Show, Eq) + deriving (Show, Eq, Ord) data UnqualifiedName = UnqualifiedName Text deriving (Show, Eq) @@ -165,7 +168,7 @@ fromP = reserved "from" *> ((:) <$> nonJoinTref <*> sepByComma joinP) joinConditionP :: Parser JoinCondition joinConditionP = do - (JoinOn <$> (reserved "on" *> scalarExprP)) <|> + (JoinOn <$> (reserved "on" *> (JoinOnCondition <$> scalarExprP))) <|> JoinUsing <$> (reserved "using" *> parens (sepBy1 unqualifiedNameP comma)) joinTypeP :: Parser JoinType diff --git a/src/bin/TutorialD/Interpreter/RelationalExpr.hs b/src/bin/TutorialD/Interpreter/RelationalExpr.hs index 37b84e25..890a0d88 100644 --- a/src/bin/TutorialD/Interpreter/RelationalExpr.hs +++ b/src/bin/TutorialD/Interpreter/RelationalExpr.hs @@ -82,9 +82,9 @@ renameP = do reservedOp "rename" renameList <- braces (sepBy renameClauseP comma) case renameList of - [] -> pure (Restrict TruePredicate) --no-op when rename list is empty + [] -> pure id renames -> - pure $ \expr -> foldl (\acc (oldAttr, newAttr) -> Rename oldAttr newAttr acc) expr renames + pure $ Rename (S.fromList renames) whereClauseP :: RelationalMarkerExpr a => Parser (RelationalExprBase a -> RelationalExprBase a) whereClauseP = reservedOp "where" *> (Restrict <$> restrictionPredicateP) diff --git a/src/bin/TutorialD/Printer.hs b/src/bin/TutorialD/Printer.hs index dd391ca5..d5f6d6aa 100644 --- a/src/bin/TutorialD/Printer.hs +++ b/src/bin/TutorialD/Printer.hs @@ -85,7 +85,7 @@ instance Pretty RelationalExpr where pretty (MakeStaticRelation attrs tupSet) = "relation" <> prettyBracesList (A.toList attrs) <> prettyBracesList (asList tupSet) pretty (Union a b) = parens $ pretty' a <+> "union" <+> pretty' b pretty (Join a b) = parens $ pretty' a <+> "join" <+> pretty' b - pretty (Rename n1 n2 relExpr) = parens $ pretty relExpr <+> "rename" <+> braces (pretty n1 <+> "as" <+> pretty n2) + pretty (Rename attrs relExpr) = parens $ pretty relExpr <+> "rename" <+> prettyBracesList (map RenameTuple (S.toList attrs)) pretty (Difference a b) = parens $ pretty' a <+> "minus" <+> pretty' b pretty (Group attrNames attrName relExpr) = parens $ pretty relExpr <+> "group" <+> parens (pretty attrNames <+> "as" <+> pretty attrName) pretty (Ungroup attrName relExpr) = parens $ pretty' relExpr <+> "ungroup" <+> pretty attrName @@ -146,6 +146,11 @@ instance Pretty AtomType where instance Pretty ExtendTupleExpr where pretty (AttributeExtendTupleExpr attrName atomExpr) = pretty attrName <> ":=" <> pretty atomExpr +newtype RenameTuple = RenameTuple { _unRenameTuple :: (AttributeName, AttributeName) } + +instance Pretty RenameTuple where + pretty (RenameTuple (n1, n2)) = pretty n1 <+> "as" <+> pretty n2 + instance Pretty RestrictionPredicateExpr where pretty TruePredicate = "true" diff --git a/src/lib/ProjectM36/AtomFunctions/Primitive.hs b/src/lib/ProjectM36/AtomFunctions/Primitive.hs index 5350c549..9252a339 100644 --- a/src/lib/ProjectM36/AtomFunctions/Primitive.hs +++ b/src/lib/ProjectM36/AtomFunctions/Primitive.hs @@ -43,7 +43,7 @@ primitiveAtomFunctions = HS.fromList [ funcBody = body $ relationAtomFunc relationMin }, Function { funcName = "eq", - funcType = [IntegerAtomType, IntegerAtomType, BoolAtomType], + funcType = [TypeVariableType "a", TypeVariableType "a", BoolAtomType], funcBody = body $ \case [i1,i2] -> pure (BoolAtom (i1 == i2)) _ -> Left AtomFunctionTypeMismatchError diff --git a/src/lib/ProjectM36/Base.hs b/src/lib/ProjectM36/Base.hs index 219cacaf..c57922e6 100644 --- a/src/lib/ProjectM36/Base.hs +++ b/src/lib/ProjectM36/Base.hs @@ -233,7 +233,7 @@ data RelationalExprBase a = --- | Create a join of two relational expressions. The join occurs on attributes which are identical. If the expressions have no overlapping attributes, the join becomes a cross-product of both tuple sets. Join (RelationalExprBase a) (RelationalExprBase a) | --- | Rename an attribute (first argument) to another (second argument). - Rename AttributeName AttributeName (RelationalExprBase a) | + Rename (S.Set (AttributeName, AttributeName)) (RelationalExprBase a) | --- | Return a relation containing all tuples of the first argument which do not appear in the second argument (minus). Difference (RelationalExprBase a) (RelationalExprBase a) | --- | Create a sub-relation composed of the first argument's attributes which will become an attribute of the result expression. The unreferenced attributes are not altered in the result but duplicate tuples in the projection of the expression minus the attribute names are compressed into one. For more information, diff --git a/src/lib/ProjectM36/HashSecurely.hs b/src/lib/ProjectM36/HashSecurely.hs index 78ef62ad..2ca45b9e 100644 --- a/src/lib/ProjectM36/HashSecurely.hs +++ b/src/lib/ProjectM36/HashSecurely.hs @@ -25,7 +25,6 @@ import qualified Data.Set as S import Data.Time.Calendar import Data.Time.Clock import Codec.Winery (Serialise) -import Data.Int (Int64) newtype SecureHash = SecureHash { _unSecureHash :: B.ByteString } deriving (Serialise, Show, Eq) @@ -86,8 +85,8 @@ instance HashBytes a => HashBytes (RelationalExprBase a) where hashBytesL ctx "Union" [SHash exprA, SHash exprB] hashBytes (Join exprA exprB) ctx = hashBytesL ctx "Join" [SHash exprA, SHash exprB] - hashBytes (Rename nameA nameB expr) ctx = - hashBytesL ctx "Rename" [SHash nameA, SHash nameB, SHash expr] + hashBytes (Rename attrs expr) ctx = + hashBytesL ctx "Rename" [SHash attrs, SHash expr] hashBytes (Difference exprA exprB) ctx = hashBytesL ctx "Difference" [SHash exprA, SHash exprB] hashBytes (Group names name expr) ctx = @@ -116,6 +115,10 @@ instance HashBytes a => HashBytes (ExtendTupleExprBase a) where hashBytes (AttributeExtendTupleExpr name expr) ctx = hashBytesL ctx "AttributeExtendTupleExpr" [SHash name, SHash expr] +instance HashBytes (S.Set (AttributeName, AttributeName)) where + hashBytes attrs ctx = + hashBytesL ctx "RenameAttrSet" (V.concatMap (\(a,b) -> V.fromList [SHash a, SHash b]) (V.fromList $ S.toList attrs)) + instance HashBytes a => HashBytes (WithNameExprBase a) where hashBytes (WithNameExpr rv marker) ctx = hashBytesL ctx "WithNameExpr" [SHash rv, SHash marker] diff --git a/src/lib/ProjectM36/IsomorphicSchema.hs b/src/lib/ProjectM36/IsomorphicSchema.hs index cf2db2f3..a59fb20e 100644 --- a/src/lib/ProjectM36/IsomorphicSchema.hs +++ b/src/lib/ProjectM36/IsomorphicSchema.hs @@ -163,7 +163,7 @@ relExprMogrify func (Join exprA exprB) = do exA <- func exprA exB <- func exprB func (Join exA exB) -relExprMogrify func (Rename n1 n2 expr) = func expr >>= \ex -> func (Rename n1 n2 ex) +relExprMogrify func (Rename attrs expr) = func expr >>= \ex -> func (Rename attrs ex) relExprMogrify func (Difference exprA exprB) = do exA <- func exprA exB <- func exprB diff --git a/src/lib/ProjectM36/Key.hs b/src/lib/ProjectM36/Key.hs index d6dfbfbb..455c09ca 100644 --- a/src/lib/ProjectM36/Key.hs +++ b/src/lib/ProjectM36/Key.hs @@ -56,7 +56,7 @@ inclusionDependencyForForeignKey (rvA, attrsA) (rvB, attrsB) = folder (attrExpected, attrExisting) expr = if attrExpected == attrExisting then expr else - Rename attrExisting attrExpected expr + Rename (S.singleton (attrExisting, attrExpected)) expr -- if the constraint is a foreign key constraint, then return the relations and attributes involved - this only detects foreign keys created with `databaseContextExprForForeignKey` isForeignKeyFor :: InclusionDependency -> (RelVarName, [AttributeName]) -> (RelVarName, [AttributeName]) -> Bool diff --git a/src/lib/ProjectM36/NormalizeExpr.hs b/src/lib/ProjectM36/NormalizeExpr.hs index bf9e6d01..8b646a4c 100644 --- a/src/lib/ProjectM36/NormalizeExpr.hs +++ b/src/lib/ProjectM36/NormalizeExpr.hs @@ -29,8 +29,8 @@ processRelationalExpr (RelationVariable rv ()) = RelationVariable rv <$> askMark processRelationalExpr (Project attrNames expr) = Project <$> processAttributeNames attrNames <*> processRelationalExpr expr processRelationalExpr (Union exprA exprB) = Union <$> processRelationalExpr exprA <*> processRelationalExpr exprB processRelationalExpr (Join exprA exprB) = Join <$> processRelationalExpr exprA <*> processRelationalExpr exprB -processRelationalExpr (Rename attrA attrB expr) = - Rename attrA attrB <$> processRelationalExpr expr +processRelationalExpr (Rename attrs expr) = + Rename attrs <$> processRelationalExpr expr processRelationalExpr (Difference exprA exprB) = Difference <$> processRelationalExpr exprA <*> processRelationalExpr exprB processRelationalExpr (Group attrNames attrName expr) = Group <$> processAttributeNames attrNames <*> pure attrName <*> processRelationalExpr expr processRelationalExpr (Ungroup attrName expr) = Ungroup attrName <$> processRelationalExpr expr diff --git a/src/lib/ProjectM36/Relation.hs b/src/lib/ProjectM36/Relation.hs index 58257fb5..14ba69f1 100644 --- a/src/lib/ProjectM36/Relation.hs +++ b/src/lib/ProjectM36/Relation.hs @@ -94,6 +94,11 @@ project attrNames rel@(Relation _ tupSet) = do newTupleList <- mapM (tupleProject newAttrs) (asList tupSet) pure (Relation newAttrs (RelationTupleSet (HS.toList (HS.fromList newTupleList)))) +renameMany :: S.Set (AttributeName, AttributeName) -> Relation -> Either RelationalError Relation +renameMany renames rel = foldM folder rel (S.toList renames) + where + folder r (oldName, newName) = rename oldName newName r + rename :: AttributeName -> AttributeName -> Relation -> Either RelationalError Relation rename oldAttrName newAttrName rel@(Relation oldAttrs oldTupSet) | not attributeValid = Left $ AttributeNamesMismatchError (S.singleton oldAttrName) diff --git a/src/lib/ProjectM36/RelationalExpression.hs b/src/lib/ProjectM36/RelationalExpression.hs index 48be18d1..30650b02 100644 --- a/src/lib/ProjectM36/RelationalExpression.hs +++ b/src/lib/ProjectM36/RelationalExpression.hs @@ -335,7 +335,7 @@ evalGraphRefDatabaseContextExpr (Update relVarName atomExprMap pred') = do else tmpAttrName updateAttr nam atomExpr = Extend (AttributeExtendTupleExpr (tmpAttr nam) atomExpr) - projectAndRename attr expr = Rename (tmpAttr attr) attr (Project (InvertedAttributeNames (S.singleton attr)) expr) + projectAndRename attr expr = Rename (S.singleton ((tmpAttr attr), attr)) (Project (InvertedAttributeNames (S.singleton attr)) expr) restrictedPortion = Restrict pred' rvExpr updated = foldr (\(oldname, atomExpr) accum -> let procAtomExpr = runProcessExprM UncommittedContextMarker (processAtomExpr atomExpr) in @@ -1111,9 +1111,9 @@ evalGraphRefRelationalExpr (Join exprA exprB) = do relA <- evalGraphRefRelationalExpr exprA relB <- evalGraphRefRelationalExpr exprB lift $ except $ join relA relB -evalGraphRefRelationalExpr (Rename oldName newName expr) = do +evalGraphRefRelationalExpr (Rename attrsSet expr) = do rel <- evalGraphRefRelationalExpr expr - lift $ except $ rename oldName newName rel + lift $ except $ renameMany attrsSet rel evalGraphRefRelationalExpr (Difference exprA exprB) = do relA <- evalGraphRefRelationalExpr exprA relB <- evalGraphRefRelationalExpr exprB @@ -1196,9 +1196,9 @@ typeForGraphRefRelationalExpr (Join exprA exprB) = do exprA' <- typeForGraphRefRelationalExpr exprA exprB' <- typeForGraphRefRelationalExpr exprB lift $ except $ join exprA' exprB' -typeForGraphRefRelationalExpr (Rename oldAttr newAttr expr) = do +typeForGraphRefRelationalExpr (Rename attrs expr) = do expr' <- typeForGraphRefRelationalExpr expr - lift $ except $ rename oldAttr newAttr expr' + lift $ except $ renameMany attrs expr' typeForGraphRefRelationalExpr (Difference exprA exprB) = do exprA' <- typeForGraphRefRelationalExpr exprA exprB' <- typeForGraphRefRelationalExpr exprB @@ -1283,7 +1283,7 @@ mkEmptyRelVars = M.map mkEmptyRelVar mkEmptyRelVar (Project attrNames expr) = Project attrNames (mkEmptyRelVar expr) mkEmptyRelVar (Union exprA exprB) = Union (mkEmptyRelVar exprA) (mkEmptyRelVar exprB) mkEmptyRelVar (Join exprA exprB) = Join (mkEmptyRelVar exprA) (mkEmptyRelVar exprB) - mkEmptyRelVar (Rename nameA nameB expr) = Rename nameA nameB (mkEmptyRelVar expr) + mkEmptyRelVar (Rename attrs expr) = Rename attrs (mkEmptyRelVar expr) mkEmptyRelVar (Difference exprA exprB) = Difference (mkEmptyRelVar exprA) (mkEmptyRelVar exprB) mkEmptyRelVar (Group attrNames attrName expr) = Group attrNames attrName (mkEmptyRelVar expr) mkEmptyRelVar (Ungroup attrName expr) = Ungroup attrName (mkEmptyRelVar expr) @@ -1363,7 +1363,7 @@ instance ResolveGraphRefTransactionMarker GraphRefRelationalExpr where resolve (Project attrNames relExpr) = Project <$> resolve attrNames <*> resolve relExpr resolve (Union exprA exprB) = Union <$> resolve exprA <*> resolve exprB resolve (Join exprA exprB) = Join <$> resolve exprA <*> resolve exprB - resolve (Rename attrA attrB expr) = Rename attrA attrB <$> resolve expr + resolve (Rename attrs expr) = Rename attrs <$> resolve expr resolve (Difference exprA exprB) = Difference <$> resolve exprA <*> resolve exprB resolve (Group namesA nameB expr) = Group <$> resolve namesA <*> pure nameB <*> resolve expr resolve (Ungroup nameA expr) = Ungroup nameA <$> resolve expr diff --git a/src/lib/ProjectM36/Shortcuts.hs b/src/lib/ProjectM36/Shortcuts.hs index 4b22d9c3..048bad64 100644 --- a/src/lib/ProjectM36/Shortcuts.hs +++ b/src/lib/ProjectM36/Shortcuts.hs @@ -118,8 +118,7 @@ tuple as' = TupleExpr (M.fromList as') rename :: RelationalExpr -> [(AttributeName,AttributeName)] -> RelationalExpr rename relExpr renameList = case renameList of [] -> Restrict TruePredicate relExpr - renames -> - foldl (\acc (old,new) -> Rename old new acc) relExpr renames + renames -> Rename (S.fromList renames) relExpr --project !! -- #a !! [#b,#c] diff --git a/src/lib/ProjectM36/StaticOptimizer.hs b/src/lib/ProjectM36/StaticOptimizer.hs index 43b1be9b..569bd026 100644 --- a/src/lib/ProjectM36/StaticOptimizer.hs +++ b/src/lib/ProjectM36/StaticOptimizer.hs @@ -506,8 +506,8 @@ applyStaticRestrictionCollapse expr = Union (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2) Join sub1 sub2 -> Join (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2) - Rename n1 n2 sub -> - Rename n1 n2 (applyStaticRestrictionCollapse sub) + Rename attrs sub -> + Rename attrs (applyStaticRestrictionCollapse sub) Difference sub1 sub2 -> Difference (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2) Group n1 n2 sub -> @@ -561,8 +561,8 @@ applyStaticRestrictionPushdown expr = case expr of Union (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2) Join sub1 sub2 -> Join (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2) - Rename n1 n2 sub -> - Rename n1 n2 (applyStaticRestrictionPushdown sub) + Rename attrs sub -> + Rename attrs (applyStaticRestrictionPushdown sub) Difference sub1 sub2 -> Difference (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2) Group n1 n2 sub -> diff --git a/src/lib/ProjectM36/TransGraphRelationalExpression.hs b/src/lib/ProjectM36/TransGraphRelationalExpression.hs index d81882ba..e9ad5794 100644 --- a/src/lib/ProjectM36/TransGraphRelationalExpression.hs +++ b/src/lib/ProjectM36/TransGraphRelationalExpression.hs @@ -72,8 +72,8 @@ processTransGraphRelationalExpr (Union exprA exprB) = Union <$> processTransGraphRelationalExpr exprA <*> processTransGraphRelationalExpr exprB processTransGraphRelationalExpr (Join exprA exprB) = Join <$> processTransGraphRelationalExpr exprA <*> processTransGraphRelationalExpr exprB -processTransGraphRelationalExpr (Rename attrName1 attrName2 expr) = - Rename attrName1 attrName2 <$> processTransGraphRelationalExpr expr +processTransGraphRelationalExpr (Rename attrs expr) = + Rename attrs <$> processTransGraphRelationalExpr expr processTransGraphRelationalExpr (Difference exprA exprB) = Difference <$> processTransGraphRelationalExpr exprA <*> processTransGraphRelationalExpr exprB processTransGraphRelationalExpr (Group transAttrNames attrName expr) = diff --git a/src/lib/ProjectM36/WithNameExpr.hs b/src/lib/ProjectM36/WithNameExpr.hs index 622a9e6a..b8127d05 100644 --- a/src/lib/ProjectM36/WithNameExpr.hs +++ b/src/lib/ProjectM36/WithNameExpr.hs @@ -31,8 +31,8 @@ substituteWithNameMacros macros (Union exprA exprB) = Union (substituteWithNameMacros macros exprA) (substituteWithNameMacros macros exprB) substituteWithNameMacros macros (Join exprA exprB) = Join (substituteWithNameMacros macros exprA) (substituteWithNameMacros macros exprB) -substituteWithNameMacros macros (Rename attrA attrB expr) = - Rename attrA attrB (substituteWithNameMacros macros expr) +substituteWithNameMacros macros (Rename attrs expr) = + Rename attrs (substituteWithNameMacros macros expr) substituteWithNameMacros macros (Difference exprA exprB) = Difference (substituteWithNameMacros macros exprA) (substituteWithNameMacros macros exprB) substituteWithNameMacros macros (Group attrs attr expr) = diff --git a/test/SQL/InterpreterTest.hs b/test/SQL/InterpreterTest.hs index 0dbbd925..36b02534 100644 --- a/test/SQL/InterpreterTest.hs +++ b/test/SQL/InterpreterTest.hs @@ -24,7 +24,7 @@ testSelect = TestCase $ do -- check that SQL and tutd compile to same thing (tgraph,transId) <- freshTransactionGraph dateExamples let p tin = parse selectP "test" tin - readTests = [{-("SELECT * FROM test", "test"), + readTests = [("SELECT * FROM test", "test"), ("SELECT a FROM test", "test{a}"), ("SELECT a FROM test where b=3","(test where b=3){a}"), ("SELECT a,b FROM test where b>3","(test where gt(@b,3)){a,b}"), @@ -35,10 +35,11 @@ testSelect = TestCase $ do ("SELECT sup.city,sup.sname FROM s AS sup","with (sup as s) ((sup rename {city as `sup.city`,sname as `sup.sname`}){`sup.city`,`sup.sname`})"), ("SELECT sup.* FROM s as sup","with (sup as s) (sup{all from sup})"), ("SELECT * FROM s NATURAL JOIN sp","s join sp"), - ("SELECT * FROM s CROSS JOIN sp", "(s rename {s# as s#_a1}) join sp"), + ("SELECT * FROM s CROSS JOIN sp", "(s rename {s# as `s.s#`}) join sp"), ("SELECT * FROM sp INNER JOIN sp USING (\"s#\")", - "(sp rename {p# as p#_a1, qty as qty_a1}) join sp"),-} - ("SELECT * FROM sp JOIN s ON s.s# = sp.s#","sp join s") + "(sp rename {p# as `sp.p#`, qty as `sp.qty`}) join sp"), + + ("SELECT * FROM sp JOIN s ON s.s# = sp.s#","((((s rename {s# as `s.s#`,sname as `s.sname`,city as `s.city`,status as `s.status`}) join (sp rename {s# as `sp.s#`,p# as `sp.p#`,qty as `sp.qty`})):{join_1:=eq(@`s.s#`,@`sp.s#`)}) where join_1=True) {all but join_1}") ] gfEnv = GraphRefRelationalExprEnv { gre_context = Just dateExamples,