relational-query 0.4.0.0 → 0.4.0.1
raw patch · 5 files changed
+226/−62 lines, 5 files
Files
- relational-query.cabal +1/−1
- src/Database/Relational/Query/Monad/Aggregate.hs +8/−5
- src/Database/Relational/Query/Projectable.hs +1/−1
- test/Lex.hs +35/−16
- test/SQLs.hs +181/−39
relational-query.cabal view
@@ -1,5 +1,5 @@ name: relational-query-version: 0.4.0.0+version: 0.4.0.1 synopsis: Typeful, Modular, Relational, algebraic query engine description: This package contiains typeful relation structure and relational-algebraic query building DSL which can
src/Database/Relational/Query/Monad/Aggregate.hs view
@@ -27,16 +27,16 @@ ) where import Data.Functor.Identity (Identity (runIdentity))+import Data.Monoid ((<>)) -import Database.Relational.Query.Internal.SQL (showStringSQL) import Database.Relational.Query.Context (Flat, Aggregated, OverWindow) import Database.Relational.Query.Projection (Projection) import qualified Database.Relational.Query.Projection as Projection import Database.Relational.Query.Component- (AggregateColumnRef, Duplication, QueryRestriction, OrderingTerms, AggregateElem, composeOver)+ (AggregateColumnRef, Duplication, QueryRestriction, OrderingTerms, AggregateElem, composeOver, showsColumnSQL) import Database.Relational.Query.Sub (SubQuery, aggregatedSubQuery, JoinProduct) import qualified Database.Relational.Query.Sub as SubQuery-import Database.Relational.Query.Projectable (PlaceHolders, SqlProjectable, unsafeProjectSql, unsafeShowSql)+import Database.Relational.Query.Projectable (PlaceHolders, SqlProjectable) import Database.Relational.Query.Monad.Class (MonadRestrict(..), MonadQualify(..), MonadPartition (..)) import Database.Relational.Query.Monad.Trans.Join (join')@@ -104,5 +104,8 @@ => Projection OverWindow a -> Window c () -> Projection c a-wp `over` win = unsafeProjectSql $ unwords [unsafeShowSql wp, showStringSQL (composeOver pt ot)] where- (((), ot), pt) = extractWindow win+wp `over` win =+ Projection.unsafeFromSqlTerms+ [ showsColumnSQL c <> composeOver pt ot+ | c <- Projection.columns wp+ ] where (((), ot), pt) = extractWindow win
src/Database/Relational/Query/Projectable.hs view
@@ -188,7 +188,7 @@ -- | Unsafely make projection unary operator from SQL keyword. unsafeUniOp :: (ProjectableShowSql p0, SqlProjectable p1) => (Keyword -> Keyword) -> p0 a -> p1 b-unsafeUniOp u = unsafeProjectSql . SQL.strUniOp u . unsafeShowSql+unsafeUniOp u = unsafeProjectSql' . u . unsafeShowSql' unsafeFlatUniOp :: (SqlProjectable p, ProjectableShowSql p) => Keyword -> p a -> p b
test/Lex.hs view
@@ -3,18 +3,40 @@ import Control.Applicative ((<$>), (<*>), pure, (*>), (<*), (<|>), empty, many, some) import Control.Monad (void)-import Control.Monad.Trans.State (StateT, runStateT, get, put)+import Control.Monad.Trans.State (StateT (..), evalStateT, get, put) import Control.Monad.Trans.Class (lift) import Data.Maybe (listToMaybe, fromMaybe) import Data.Map (Map) import qualified Data.Map as Map-import Text.ParserCombinators.ReadP (ReadP, readP_to_S)-import qualified Text.ParserCombinators.ReadP as ReadP import Distribution.TestSuite (Test) import Distribution.TestSuite.Compat (prop') +type P = StateT String Maybe++satisfy' :: (Char -> Bool) -> P Char+satisfy' p = do+ s <- get+ case s of+ c:cs -> if p c+ then put cs *> pure c+ else empty+ [] -> empty++char' :: Char -> P Char+char' x = satisfy' (== x)++look' :: P String+look' = get++eof' :: P ()+eof' = do+ s <- get+ case s of+ [] -> pure ()+ _:_ -> empty+ type Var = Int data Token@@ -37,13 +59,16 @@ , varMap :: Map VarName Var } deriving Eq -type Parser = StateT QState ReadP+type Parser = StateT QState P +run' :: Parser a -> String -> Maybe (a, String)+run' p = runStateT (evalStateT p (QState { nextVar = 0, varMap = Map.empty }))+ char :: Char -> Parser Char-char = lift . ReadP.char+char = lift . char' satisfy :: (Char -> Bool) -> Parser Char-satisfy = lift . ReadP.satisfy+satisfy = lift . satisfy' quote :: Parser Char quote = char '\''@@ -58,7 +83,7 @@ symbol = Symbol <$> symbol' opCharset :: [Char]-opCharset = "=<>+-*|"+opCharset = "=<>+-*/|" op :: Parser Token op = Op <$> some (satisfy (`elem` opCharset))@@ -101,7 +126,7 @@ spaces = many space *> pure () peekChar :: Parser (Maybe Char)-peekChar = listToMaybe <$> lift ReadP.look+peekChar = listToMaybe <$> lift look' peekSatisfy :: (Char -> Bool) -> Parser Char peekSatisfy pre = do@@ -130,7 +155,7 @@ placeholder = char '?' *> pure PlaceHolder eof :: Parser ()-eof = lift ReadP.eof+eof = lift eof' token :: Parser Token token =@@ -147,12 +172,6 @@ tokens :: Parser [Token] tokens = (many $ token <* spaces) <* eof -run' :: Parser a -> String -> Maybe (a, String)-run' p =- (result <$>) . listToMaybe .- readP_to_S (runStateT p (QState { nextVar = 0, varMap = Map.empty })) where- result ((a, _s), in') = (a, in')- run :: String -> Maybe [Token] run = (fst <$>) . run' tokens @@ -165,7 +184,7 @@ eqProp' :: String -> (a -> String) -> a -> String -> Test eqProp' name t x est = prop' name (Just em) (t x `eq` est)- where em = unlines [show . run $ t x, " -- NOT EQUALS! --", show $ run est]+ where em = unlines [show $ run $ t x, " -- compares --", show $ run est] eqProp :: Show a => String -> a -> String -> Test eqProp name = eqProp' name show
test/SQLs.hs view
@@ -3,21 +3,39 @@ import Distribution.TestSuite (Test) import Distribution.TestSuite.Compat (TestList, testList) +import Control.Applicative ((<$>), (<*>))+ import Lex (eqProp) import Model -import Data.Int (Int32)+import Data.Int (Int32, Int64) import Database.Relational.Query -base :: [Test]-base =++numBin :: (Projection Flat Int32 -> Projection Flat Int32 -> Projection Flat r) -> Relation () r+numBin op = relation $ do+ return $ value 5 `op` value 3++_p_numBin :: (Projection Flat Int32 -> Projection Flat Int32 -> Projection Flat r) -> IO ()+_p_numBin = print . numBin++bin :: [Test]+bin =+ [ eqProp "plus" (numBin (.+.)) "SELECT ALL (5 + 3) AS f0"+ , eqProp "minus" (numBin (.-.)) "SELECT ALL (5 - 3) AS f0"+ , eqProp "mult" (numBin (.*.)) "SELECT ALL (5 * 3) AS f0"+ , eqProp "div" (numBin (./.)) "SELECT ALL (5 / 3) AS f0"+ ]++tables :: [Test]+tables = [ eqProp "setA" setA "SELECT int_a0, str_a1, str_a2 FROM TEST.set_a" , eqProp "setB" setB "SELECT int_b0, may_str_b1, str_b2 FROM TEST.set_b" , eqProp "setC" setC "SELECT int_c0, str_c1, int_c2, may_str_c3 FROM TEST.set_c" ] -_p_base :: IO ()-_p_base = mapM_ print [show setA, show setB, show setC]+_p_tables :: IO ()+_p_tables = mapM_ print [show setA, show setB, show setC] cross :: Relation () (SetA, SetB) cross = setA `inner` setB `on'` []@@ -37,15 +55,25 @@ directJoins :: [Test] directJoins = [ eqProp "cross" cross- "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 FROM TEST.set_a T0 INNER JOIN TEST.set_b T1 ON (0=0)"+ "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, \+ \ T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 \+ \ FROM TEST.set_a T0 INNER JOIN TEST.set_b T1 ON (0=0)" , eqProp "inner" innerX- "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 FROM TEST.set_a T0 INNER JOIN TEST.set_b T1 ON (T0.int_a0 = T1.int_b0)"+ "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, \+ \ T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 \+ \ FROM TEST.set_a T0 INNER JOIN TEST.set_b T1 ON (T0.int_a0 = T1.int_b0)" , eqProp "left" leftX- "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 FROM TEST.set_a T0 LEFT JOIN TEST.set_b T1 ON (T0.str_a1 = T1.may_str_b1)"+ "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, \+ \ T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 \+ \ FROM TEST.set_a T0 LEFT JOIN TEST.set_b T1 ON (T0.str_a1 = T1.may_str_b1)" , eqProp "right" rightX- "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 FROM TEST.set_a T0 RIGHT JOIN TEST.set_b T1 ON (T0.int_a0 = T1.int_b0)"+ "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, \+ \ T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 \+ \ FROM TEST.set_a T0 RIGHT JOIN TEST.set_b T1 ON (T0.int_a0 = T1.int_b0)" , eqProp "full" fullX- "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 FROM TEST.set_a T0 FULL JOIN TEST.set_b T1 ON (T0.int_a0 = T1.int_b0)"+ "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, \+ \ T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 \+ \ FROM TEST.set_a T0 FULL JOIN TEST.set_b T1 ON (T0.int_a0 = T1.int_b0)" ] _p_directJoins :: IO ()@@ -73,12 +101,23 @@ return $ Abc |$| a |*| b |*| c join3s :: [Test]-join3s = [ eqProp "join-3 left" j3left- "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5, T2.int_c0 AS f6, T2.str_c1 AS f7, T2.int_c2 AS f8, T2.may_str_c3 AS f9 FROM (TEST.set_a T0 LEFT JOIN TEST.set_b T1 ON (T0.str_a2 = T1.str_b2)) LEFT JOIN TEST.set_c T2 ON (T1.int_b0 = T2.int_c0)"+join3s =+ [ eqProp "join-3 left" j3left+ "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, \+ \ T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5, \+ \ T2.int_c0 AS f6, T2.str_c1 AS f7, T2.int_c2 AS f8, T2.may_str_c3 AS f9 \+ \ FROM (TEST.set_a T0 LEFT JOIN TEST.set_b T1 ON (T0.str_a2 = T1.str_b2)) \+ \ LEFT JOIN TEST.set_c T2 ON (T1.int_b0 = T2.int_c0)" - , eqProp "join-3 right" j3right- "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, T3.f0 AS f3, T3.f1 AS f4, T3.f2 AS f5, T3.f3 AS f6, T3.f4 AS f7, T3.f5 AS f8, T3.f6 AS f9 FROM TEST.set_a T0 INNER JOIN (SELECT ALL T1.int_b0 AS f0, T1.may_str_b1 AS f1, T1.str_b2 AS f2, T2.int_c0 AS f3, T2.str_c1 AS f4, T2.int_c2 AS f5, T2.may_str_c3 AS f6 FROM TEST.set_b T1 FULL JOIN TEST.set_c T2 ON (T1.int_b0 = T2.int_c0)) T3 ON (T0.str_a2 = T3.f2)"- ]+ , eqProp "join-3 right" j3right+ "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, \+ \ T3.f0 AS f3, T3.f1 AS f4, T3.f2 AS f5, T3.f3 AS f6, T3.f4 AS f7, T3.f5 AS f8, T3.f6 AS f9 \+ \ FROM TEST.set_a T0 \+ \ INNER JOIN (SELECT ALL T1.int_b0 AS f0, T1.may_str_b1 AS f1, T1.str_b2 AS f2, \+ \ T2.int_c0 AS f3, T2.str_c1 AS f4, T2.int_c2 AS f5, T2.may_str_c3 AS f6 \+ \ FROM TEST.set_b T1 FULL JOIN TEST.set_c T2 ON (T1.int_b0 = T2.int_c0)) T3 \+ \ ON (T0.str_a2 = T3.f2)"+ ] _p_j3s :: IO () _p_j3s = mapM_ print [show j3left, show j3right]@@ -102,15 +141,105 @@ return $ fromMaybe (value 1) (a ?! intA0') >< b maybes :: [Test]-maybes = [ eqProp "isJust" justX- "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 FROM TEST.set_a T0 LEFT JOIN TEST.set_b T1 ON (0=0) WHERE ((NOT (T1.int_b0 IS NULL)) OR (T0.int_a0 = 1))"- , eqProp "fromMaybe" maybeX- "SELECT ALL CASE WHEN (T0.int_a0 IS NULL) THEN 1 ELSE T0.int_a0 END AS f0, T1.int_b0 AS f1, T1.may_str_b1 AS f2, T1.str_b2 AS f3 FROM TEST.set_a T0 RIGHT JOIN TEST.set_b T1 ON (0=0) WHERE (T0.str_a2 = T1.may_str_b1)"- ]+maybes =+ [ eqProp "isJust" justX+ "SELECT ALL T0.int_a0 AS f0, T0.str_a1 AS f1, T0.str_a2 AS f2, \+ \ T1.int_b0 AS f3, T1.may_str_b1 AS f4, T1.str_b2 AS f5 \+ \ FROM TEST.set_a T0 LEFT JOIN TEST.set_b T1 ON (0=0) \+ \ WHERE ((NOT (T1.int_b0 IS NULL)) OR (T0.int_a0 = 1))"+ , eqProp "fromMaybe" maybeX+ "SELECT ALL CASE WHEN (T0.int_a0 IS NULL) THEN 1 ELSE T0.int_a0 END AS f0, \+ \ T1.int_b0 AS f1, T1.may_str_b1 AS f2, T1.str_b2 AS f3 \+ \ FROM TEST.set_a T0 RIGHT JOIN TEST.set_b T1 ON (0=0) WHERE (T0.str_a2 = T1.may_str_b1)"+ ] _p_maybes :: IO () _p_maybes = mapM_ print [show justX, show maybeX] +groupX :: Relation () (String, Int64)+groupX = aggregateRelation $ do+ c <- query setC++ gc1 <- groupBy $ c ! strC1'+ return $ gc1 >< count (c ! intC0')++cubeX :: Relation () ((Maybe String, Maybe (Int64, Maybe String)), Maybe Int32)+cubeX = aggregateRelation $ do+ c <- query setC++ gCube <- groupBy' . cube $ do+ (><)+ <$> bkey (c ! strC1')+ <*> bkey (c ! intC2' >< c ! mayStrC3')+ return $ gCube >< sum' (c ! intC0')++groupingSetsX :: Relation () (((Maybe String, Maybe (Maybe String)), Maybe Int64), Maybe Int64)+groupingSetsX = aggregateRelation $ do+ c <- query setC++ gs <- groupBy' . groupingSets $ do+ s1 <- set $ do+ gRollup <- key' . rollup $ do+ (><)+ <$> bkey (c ! strC1')+ <*> bkey (c ! mayStrC3')+ gc2 <- key $ c ! intC2'+ return $ gRollup >< gc2+ s2 <- set . key $ c ! intC2'+ return $ s1 >< s2++ return gs+++groups :: [Test]+groups =+ [ eqProp "group" groupX+ "SELECT ALL T0.str_c1 AS f0, COUNT(T0.int_c0) AS f1 \+ \ FROM TEST.set_c T0 GROUP BY T0.str_c1"+ , eqProp "cube" cubeX+ "SELECT ALL T0.str_c1 AS f0, T0.int_c2 AS f1, T0.may_str_c3 AS f2, SUM(T0.int_c0) AS f3 \+ \ FROM TEST.set_c T0 GROUP BY CUBE ((T0.str_c1), (T0.int_c2, T0.may_str_c3))"+ , eqProp "groupingSets" groupingSetsX+ "SELECT ALL T0.str_c1 AS f0, T0.may_str_c3 AS f1, T0.int_c2 AS f2, T0.int_c2 AS f3 \+ \ FROM TEST.set_c T0 GROUP BY \+ \ GROUPING SETS ((ROLLUP ((T0.str_c1), (T0.may_str_c3)), T0.int_c2), (T0.int_c2))"+ ]++_p_groups :: IO ()+_p_groups = mapM_ print [show groupX, show cubeX, show groupingSetsX]++partitionX :: Relation () (String, Int64)+partitionX = relation $ do+ c <- query setC++ return $ (c ! strC1') >< rank `over` do+ partitionBy $ c ! strC1'+ orderBy (c ! intC2') Asc++partitionY :: Relation () (String, (Int64, Maybe Int32))+partitionY = relation $ do+ c <- query setC++ return $ (c ! strC1') >< (rank >< sum' (c ! intC0'))`over` do+ partitionBy $ c ! strC1'+ orderBy (c ! intC2') Asc++partitions :: [Test]+partitions =+ [ eqProp "partition 0" partitionX+ "SELECT ALL T0.str_c1 AS f0, \+ \ RANK() OVER (PARTITION BY T0.str_c1 ORDER BY T0.int_c2 ASC) AS f1 \+ \ FROM TEST.set_c T0"+ , eqProp "partition 1" partitionY+ "SELECT ALL T0.str_c1 AS f0, \+ \ RANK() OVER (PARTITION BY T0.str_c1 ORDER BY T0.int_c2 ASC) AS f1, \+ \ SUM(T0.int_c0) OVER (PARTITION BY T0.str_c1 ORDER BY T0.int_c2 ASC) AS f2 \+ \ FROM TEST.set_c T0"+ ]++_p_partitions :: IO ()+_p_partitions = mapM_ print [show partitionX, show partitionY]+ setAFromB :: Pi SetB SetA setAFromB = SetA |$| intB0' |*| strB2' |*| strB2' @@ -132,19 +261,27 @@ intersectX = setA `intersect` aFromB exps :: [Test]-exps = [ eqProp "union" unionX- "SELECT int_a0 AS f0, str_a1 AS f1, str_a2 AS f2 FROM TEST.set_a UNION SELECT ALL T0.int_b0 AS f0, T0.str_b2 AS f1, T0.str_b2 AS f2 FROM TEST.set_b T0"- , eqProp "unionAll" unionAllX- "SELECT int_a0 AS f0, str_a1 AS f1, str_a2 AS f2 FROM TEST.set_a UNION ALL SELECT ALL T0.int_b0 AS f0, T0.str_b2 AS f1, T0.str_b2 AS f2 FROM TEST.set_b T0"- , eqProp "except" exceptX- "SELECT int_a0 AS f0, str_a1 AS f1, str_a2 AS f2 FROM TEST.set_a EXCEPT SELECT ALL T0.int_b0 AS f0, T0.str_b2 AS f1, T0.str_b2 AS f2 FROM TEST.set_b T0"- , eqProp "intersect" intersectX- "SELECT int_a0 AS f0, str_a1 AS f1, str_a2 AS f2 FROM TEST.set_a INTERSECT SELECT ALL T0.int_b0 AS f0, T0.str_b2 AS f1, T0.str_b2 AS f2 FROM TEST.set_b T0"- ]+exps =+ [ eqProp "union" unionX+ "SELECT int_a0 AS f0, str_a1 AS f1, str_a2 AS f2 FROM TEST.set_a UNION \+ \SELECT ALL T0.int_b0 AS f0, T0.str_b2 AS f1, T0.str_b2 AS f2 FROM TEST.set_b T0"+ , eqProp "unionAll" unionAllX+ "SELECT int_a0 AS f0, str_a1 AS f1, str_a2 AS f2 FROM TEST.set_a UNION ALL \+ \SELECT ALL T0.int_b0 AS f0, T0.str_b2 AS f1, T0.str_b2 AS f2 FROM TEST.set_b T0"+ , eqProp "except" exceptX+ "SELECT int_a0 AS f0, str_a1 AS f1, str_a2 AS f2 FROM TEST.set_a EXCEPT \+ \SELECT ALL T0.int_b0 AS f0, T0.str_b2 AS f1, T0.str_b2 AS f2 FROM TEST.set_b T0"+ , eqProp "intersect" intersectX+ "SELECT int_a0 AS f0, str_a1 AS f1, str_a2 AS f2 FROM TEST.set_a INTERSECT \+ \SELECT ALL T0.int_b0 AS f0, T0.str_b2 AS f1, T0.str_b2 AS f2 FROM TEST.set_b T0"+ ] insertX :: Insert SetA insertX = derivedInsert id' +insertQueryX :: InsertQuery ()+insertQueryX = derivedInsertQuery setAFromB setA+ updateKeyX :: KeyUpdate Int32 SetA updateKeyX = primaryUpdate tableOfSetA @@ -160,16 +297,21 @@ return unitPlaceHolder effs :: [Test]-effs = [ eqProp "insert" insertX- "INSERT INTO TEST.set_a (int_a0, str_a1, str_a2) VALUES (?, ?, ?)"- , eqProp "updateKey" updateKeyX- "UPDATE TEST.set_a SET str_a1 = ?, str_a2 = ? WHERE int_a0 = ?"- , eqProp "update" updateX- "UPDATE TEST.set_a SET str_a2 = 'X' WHERE (str_a1 = 'A')"- , eqProp "delete" deleteX- "DELETE FROM TEST.set_a WHERE (str_a1 = 'A')"- ]+effs =+ [ eqProp "insert" insertX+ "INSERT INTO TEST.set_a (int_a0, str_a1, str_a2) VALUES (?, ?, ?)"+ , eqProp "insertQuery" insertQueryX+ "INSERT INTO TEST.set_b (int_b0, str_b2, str_b2) SELECT int_a0, str_a1, str_a2 FROM TEST.set_a"+ , eqProp "updateKey" updateKeyX+ "UPDATE TEST.set_a SET str_a1 = ?, str_a2 = ? WHERE int_a0 = ?"+ , eqProp "update" updateX+ "UPDATE TEST.set_a SET str_a2 = 'X' WHERE (str_a1 = 'A')"+ , eqProp "delete" deleteX+ "DELETE FROM TEST.set_a WHERE (str_a1 = 'A')"+ ] tests :: TestList tests =- testList $ concat [base, directJoins, join3s, maybes, exps, effs]+ testList+ $ concat [bin, tables, directJoins, join3s, maybes,+ groups, partitions, exps, effs]