packages feed

relational-query 0.4.0.0 → 0.4.0.1

raw patch · 5 files changed

+226/−62 lines, 5 files

Files

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]