比较运算符
This commit is contained in:
1 parent
df47f7da27
commit
9b1c835202
14 files changed
+192
-2
No files matched your search
@@ -327,6 +327,9 @@ substSubqueries db e env = case e of
|
||||
Gt a b -> bin Gt a b
|
||||
Lt a b -> bin Lt a b
|
||||
Eq a b -> bin Eq a b
|
||||
GtE a b -> bin GtE a b
|
||||
LtE a b -> bin LtE a b
|
||||
NotEq a b -> bin NotEq a b
|
||||
And a b -> bin And a b
|
||||
Or a b -> bin Or a b
|
||||
IsNull a -> un IsNull a
|
||||
|
||||
@@ -28,6 +28,9 @@ evalExpr (Neg a) row = do
|
||||
evalExpr (Gt a b) row = liftOperator OpGt a b row
|
||||
evalExpr (Lt a b) row = liftOperator OpLt a b row
|
||||
evalExpr (Eq a b) row = liftOperator OpEq a b row
|
||||
evalExpr (GtE a b) row = liftOperator OpGtE a b row
|
||||
evalExpr (LtE a b) row = liftOperator OpLtE a b row
|
||||
evalExpr (NotEq a b) row = liftOperator OpNe a b row
|
||||
evalExpr (And a b) row = liftOperator OpAnd a b row
|
||||
evalExpr (Or a b) row = liftOperator OpOr a b row
|
||||
evalExpr (IsNull a) row = do
|
||||
@@ -61,6 +64,9 @@ colsInExpr (Neg a) = colsInExpr a
|
||||
colsInExpr (Gt a b) = colsInExpr a ++ colsInExpr b
|
||||
colsInExpr (Lt a b) = colsInExpr a ++ colsInExpr b
|
||||
colsInExpr (Eq a b) = colsInExpr a ++ colsInExpr b
|
||||
colsInExpr (GtE a b) = colsInExpr a ++ colsInExpr b
|
||||
colsInExpr (LtE a b) = colsInExpr a ++ colsInExpr b
|
||||
colsInExpr (NotEq a b) = colsInExpr a ++ colsInExpr b
|
||||
colsInExpr (And a b) = colsInExpr a ++ colsInExpr b
|
||||
colsInExpr (Or a b) = colsInExpr a ++ colsInExpr b
|
||||
colsInExpr (IsNull a) = colsInExpr a
|
||||
@@ -87,6 +93,9 @@ hasDivision e = case e of
|
||||
Gt a b -> both a b
|
||||
Lt a b -> both a b
|
||||
Eq a b -> both a b
|
||||
GtE a b -> both a b
|
||||
LtE a b -> both a b
|
||||
NotEq a b -> both a b
|
||||
And a b -> both a b
|
||||
Or a b -> both a b
|
||||
_ -> False
|
||||
@@ -109,6 +118,9 @@ hasSubquery e = case e of
|
||||
Gt a b -> both a b
|
||||
Lt a b -> both a b
|
||||
Eq a b -> both a b
|
||||
GtE a b -> both a b
|
||||
LtE a b -> both a b
|
||||
NotEq a b -> both a b
|
||||
And a b -> both a b
|
||||
Or a b -> both a b
|
||||
IsNull a -> hasSubquery a
|
||||
@@ -140,6 +152,9 @@ aggregatesIn e = case e of
|
||||
Gt a b -> both a b
|
||||
Lt a b -> both a b
|
||||
Eq a b -> both a b
|
||||
GtE a b -> both a b
|
||||
LtE a b -> both a b
|
||||
NotEq a b -> both a b
|
||||
And a b -> both a b
|
||||
Or a b -> both a b
|
||||
IsNull a -> aggregatesIn a
|
||||
@@ -165,6 +180,9 @@ bareColumns e = case e of
|
||||
Gt a b -> both a b
|
||||
Lt a b -> both a b
|
||||
Eq a b -> both a b
|
||||
GtE a b -> both a b
|
||||
LtE a b -> both a b
|
||||
NotEq a b -> both a b
|
||||
And a b -> both a b
|
||||
Or a b -> both a b
|
||||
IsNull a -> bareColumns a
|
||||
|
||||
@@ -62,6 +62,9 @@ scaleRows db p x rows = case p of
|
||||
_ -> quarter
|
||||
Gt _ _ -> quarter
|
||||
Lt _ _ -> quarter
|
||||
GtE _ _ -> quarter
|
||||
LtE _ _ -> quarter
|
||||
NotEq _ _ -> quarter
|
||||
And a b -> max 1 (min (scaleRows db a x rows) (scaleRows db b x rows))
|
||||
_ -> quarter
|
||||
where
|
||||
|
||||
@@ -144,6 +144,9 @@ foldConstants (Neg a) = foldNode (Neg (foldConstants a))
|
||||
foldConstants (Gt a b) = foldNode (Gt (foldConstants a) (foldConstants b))
|
||||
foldConstants (Lt a b) = foldNode (Lt (foldConstants a) (foldConstants b))
|
||||
foldConstants (Eq a b) = foldNode (Eq (foldConstants a) (foldConstants b))
|
||||
foldConstants (GtE a b) = foldNode (GtE (foldConstants a) (foldConstants b))
|
||||
foldConstants (LtE a b) = foldNode (LtE (foldConstants a) (foldConstants b))
|
||||
foldConstants (NotEq a b) = foldNode (NotEq (foldConstants a) (foldConstants b))
|
||||
foldConstants (And a b) = foldNode (And (foldConstants a) (foldConstants b))
|
||||
foldConstants (Or a b) = foldNode (Or (foldConstants a) (foldConstants b))
|
||||
foldConstants e = e
|
||||
@@ -180,6 +183,32 @@ rewriteNode db (Filter (Lt (Col k) (LitInt v)) (Scan mAlias t Nothing))
|
||||
| worthRange db t (unqualify mAlias k) = Range mAlias t (unqualify mAlias k) Nothing (Just (VInt v, False))
|
||||
rewriteNode db (Filter (Lt (Col k) (LitStr v)) (Scan mAlias t Nothing))
|
||||
| worthRange db t (unqualify mAlias k) = Range mAlias t (unqualify mAlias k) Nothing (Just (VStr v, False))
|
||||
rewriteNode db (Filter (And (GtE (Col k1) (LitInt lo)) (LtE (Col k2) (LitInt hi))) (Scan mAlias t Nothing))
|
||||
| k1 == k2 && worthRange db t (unqualify mAlias k1) =
|
||||
Range mAlias t (unqualify mAlias k1) (Just (VInt lo, True)) (Just (VInt hi, True))
|
||||
rewriteNode db (Filter (And (GtE (Col k1) (LitInt lo)) (Lt (Col k2) (LitInt hi))) (Scan mAlias t Nothing))
|
||||
| k1 == k2 && worthRange db t (unqualify mAlias k1) =
|
||||
Range mAlias t (unqualify mAlias k1) (Just (VInt lo, True)) (Just (VInt hi, False))
|
||||
rewriteNode db (Filter (And (Gt (Col k1) (LitInt lo)) (LtE (Col k2) (LitInt hi))) (Scan mAlias t Nothing))
|
||||
| k1 == k2 && worthRange db t (unqualify mAlias k1) =
|
||||
Range mAlias t (unqualify mAlias k1) (Just (VInt lo, False)) (Just (VInt hi, True))
|
||||
rewriteNode db (Filter (And (GtE (Col k1) (LitStr lo)) (LtE (Col k2) (LitStr hi))) (Scan mAlias t Nothing))
|
||||
| k1 == k2 && worthRange db t (unqualify mAlias k1) =
|
||||
Range mAlias t (unqualify mAlias k1) (Just (VStr lo, True)) (Just (VStr hi, True))
|
||||
rewriteNode db (Filter (And (GtE (Col k1) (LitStr lo)) (Lt (Col k2) (LitStr hi))) (Scan mAlias t Nothing))
|
||||
| k1 == k2 && worthRange db t (unqualify mAlias k1) =
|
||||
Range mAlias t (unqualify mAlias k1) (Just (VStr lo, True)) (Just (VStr hi, False))
|
||||
rewriteNode db (Filter (And (Gt (Col k1) (LitStr lo)) (LtE (Col k2) (LitStr hi))) (Scan mAlias t Nothing))
|
||||
| k1 == k2 && worthRange db t (unqualify mAlias k1) =
|
||||
Range mAlias t (unqualify mAlias k1) (Just (VStr lo, False)) (Just (VStr hi, True))
|
||||
rewriteNode db (Filter (GtE (Col k) (LitInt v)) (Scan mAlias t Nothing))
|
||||
| worthRange db t (unqualify mAlias k) = Range mAlias t (unqualify mAlias k) (Just (VInt v, True)) Nothing
|
||||
rewriteNode db (Filter (GtE (Col k) (LitStr v)) (Scan mAlias t Nothing))
|
||||
| worthRange db t (unqualify mAlias k) = Range mAlias t (unqualify mAlias k) (Just (VStr v, True)) Nothing
|
||||
rewriteNode db (Filter (LtE (Col k) (LitInt v)) (Scan mAlias t Nothing))
|
||||
| worthRange db t (unqualify mAlias k) = Range mAlias t (unqualify mAlias k) Nothing (Just (VInt v, True))
|
||||
rewriteNode db (Filter (LtE (Col k) (LitStr v)) (Scan mAlias t Nothing))
|
||||
| worthRange db t (unqualify mAlias k) = Range mAlias t (unqualify mAlias k) Nothing (Just (VStr v, True))
|
||||
rewriteNode db (Filter p (Join kind l r c))
|
||||
| not (hasDivision p || planHasDivision (Join kind l r c)) =
|
||||
case kind of
|
||||
|
||||
@@ -44,6 +44,9 @@ rewriteAggs aggs e = case [label | (label, agg) <- aggs, agg == e] of
|
||||
Gt a b -> Gt (go a) (go b)
|
||||
Lt a b -> Lt (go a) (go b)
|
||||
Eq a b -> Eq (go a) (go b)
|
||||
GtE a b -> GtE (go a) (go b)
|
||||
LtE a b -> LtE (go a) (go b)
|
||||
NotEq a b -> NotEq (go a) (go b)
|
||||
And a b -> And (go a) (go b)
|
||||
Or a b -> Or (go a) (go b)
|
||||
IsNull a -> IsNull (go a)
|
||||
|
||||
@@ -137,6 +137,9 @@ data Operator
|
||||
| OpGt
|
||||
| OpLt
|
||||
| OpEq
|
||||
| OpGtE
|
||||
| OpLtE
|
||||
| OpNe
|
||||
| OpAnd
|
||||
| OpOr
|
||||
deriving (Show, Eq, Enum, Bounded)
|
||||
@@ -151,6 +154,9 @@ operatorSymbol OpNeg = "-"
|
||||
operatorSymbol OpGt = ">"
|
||||
operatorSymbol OpLt = "<"
|
||||
operatorSymbol OpEq = "="
|
||||
operatorSymbol OpGtE = ">="
|
||||
operatorSymbol OpLtE = "<="
|
||||
operatorSymbol OpNe = "<>"
|
||||
operatorSymbol OpAnd = "AND"
|
||||
operatorSymbol OpOr = "OR"
|
||||
|
||||
@@ -164,6 +170,9 @@ operatorOfExpr (Neg _) = Just OpNeg
|
||||
operatorOfExpr (Gt _ _) = Just OpGt
|
||||
operatorOfExpr (Lt _ _) = Just OpLt
|
||||
operatorOfExpr (Eq _ _) = Just OpEq
|
||||
operatorOfExpr (GtE _ _) = Just OpGtE
|
||||
operatorOfExpr (LtE _ _) = Just OpLtE
|
||||
operatorOfExpr (NotEq _ _) = Just OpNe
|
||||
operatorOfExpr (And _ _) = Just OpAnd
|
||||
operatorOfExpr (Or _ _) = Just OpOr
|
||||
operatorOfExpr _ = Nothing
|
||||
@@ -175,6 +184,9 @@ executeOperator op vs = case (op, vs) of
|
||||
(OpGt, [x, y]) -> compareValues (== GT) x y
|
||||
(OpLt, [x, y]) -> compareValues (< EQ) x y
|
||||
(OpEq, [x, y]) -> compareValues (== EQ) x y
|
||||
(OpGtE, [x, y]) -> compareValues (>= EQ) x y
|
||||
(OpLtE, [x, y]) -> compareValues (<= EQ) x y
|
||||
(OpNe, [x, y]) -> compareValues (/= EQ) x y
|
||||
(OpAnd, [x, y]) -> andValues x y
|
||||
(OpOr, [x, y]) -> orValues x y
|
||||
(_, [x, y]) | Just a <- arithOf op -> evalArith a x y
|
||||
|
||||
@@ -137,6 +137,9 @@ inferExpr db place env (Neg a) = do
|
||||
inferExpr db place env (Gt a b) = comparison db place env a b
|
||||
inferExpr db place env (Lt a b) = comparison db place env a b
|
||||
inferExpr db place env (Eq a b) = comparison db place env a b
|
||||
inferExpr db place env (GtE a b) = comparison db place env a b
|
||||
inferExpr db place env (LtE a b) = comparison db place env a b
|
||||
inferExpr db place env (NotEq a b) = comparison db place env a b
|
||||
inferExpr db place env (And a b) = boolean db place env a b
|
||||
inferExpr db place env (Or a b) = boolean db place env a b
|
||||
inferExpr db place env (IsNull a) = inferExpr db place env a >> Right (InferType CBool)
|
||||
@@ -597,6 +600,9 @@ resolveExpr db env outer f e = case e of
|
||||
Gt a b -> binary Gt a b
|
||||
Lt a b -> binary Lt a b
|
||||
Eq a b -> binary Eq a b
|
||||
GtE a b -> binary GtE a b
|
||||
LtE a b -> binary LtE a b
|
||||
NotEq a b -> binary NotEq a b
|
||||
And a b -> binary And a b
|
||||
Or a b -> binary Or a b
|
||||
IsNull a -> IsNull <$> resolveExpr db env outer f a
|
||||
|
||||
@@ -107,6 +107,9 @@ data Expr
|
||||
| Gt Expr Expr
|
||||
| Lt Expr Expr
|
||||
| Eq Expr Expr
|
||||
| GtE Expr Expr
|
||||
| LtE Expr Expr
|
||||
| NotEq Expr Expr
|
||||
| And Expr Expr
|
||||
| Or Expr Expr
|
||||
| IsNull Expr
|
||||
|
||||
@@ -112,7 +112,10 @@ operatorTable =
|
||||
[ [Prefix (Neg <$ symbol "-")]
|
||||
, [InfixL (Mul <$ symbol "*"), InfixL (Div <$ symbol "/")]
|
||||
, [InfixL (Add <$ symbol "+"), InfixL (Sub <$ symbol "-")]
|
||||
, [ InfixN (Gt <$ symbol ">")
|
||||
, [ InfixN (NotEq <$ try (symbol "<>" <|> symbol "!="))
|
||||
, InfixN (GtE <$ try (symbol ">="))
|
||||
, InfixN (LtE <$ try (symbol "<="))
|
||||
, InfixN (Gt <$ symbol ">")
|
||||
, InfixN (Lt <$ symbol "<")
|
||||
, InfixN (Eq <$ symbol "=")
|
||||
]
|
||||
@@ -695,6 +698,9 @@ expandExpr env e = case e of
|
||||
Gt a b -> Gt <$> go a <*> go b
|
||||
Lt a b -> Lt <$> go a <*> go b
|
||||
Eq a b -> Eq <$> go a <*> go b
|
||||
GtE a b -> GtE <$> go a <*> go b
|
||||
LtE a b -> LtE <$> go a <*> go b
|
||||
NotEq a b -> NotEq <$> go a <*> go b
|
||||
And a b -> And <$> go a <*> go b
|
||||
Or a b -> Or <$> go a <*> go b
|
||||
Neg a -> Neg <$> go a
|
||||
|
||||
@@ -518,6 +518,90 @@ main = hspec $ do
|
||||
it "reports an unknown table for a name that is not a CTE" $ do
|
||||
sql "WITH t AS (SELECT name FROM users) SELECT x.name FROM x" `shouldSatisfy` isLeft
|
||||
|
||||
describe "ChuSQL.Core.Engine (比较运算符)" $ do
|
||||
-- sql:在默认库上跑 SQL 取结果行
|
||||
let sql input = parseStatement input >>= rowsOf . runStatement testDB
|
||||
-- values:只取结果里的值
|
||||
values input = fmap (map (map snd)) (sql input)
|
||||
|
||||
it "parses >= into GtE" $ do
|
||||
parseStatement "SELECT name FROM users WHERE age >= 18"
|
||||
`shouldBe` Right (Select ["name"] (FromTable Nothing "users") (Just (GtE (Col "age") (LitInt 18))) [] [] Nothing)
|
||||
|
||||
it "parses <= into LtE" $ do
|
||||
parseStatement "SELECT name FROM users WHERE age <= 30"
|
||||
`shouldBe` Right (Select ["name"] (FromTable Nothing "users") (Just (LtE (Col "age") (LitInt 30))) [] [] Nothing)
|
||||
|
||||
it "parses <> and != into the same node" $ do
|
||||
sql "SELECT name FROM users WHERE age <> 17"
|
||||
`shouldBe` sql "SELECT name FROM users WHERE age != 17"
|
||||
values "SELECT name FROM users WHERE age <> 17 ORDER BY name"
|
||||
`shouldBe` Right [[VStr "Alice"], [VStr "Carol"]]
|
||||
|
||||
it "runs >= and <=" $ do
|
||||
values "SELECT name FROM users WHERE age >= 25 ORDER BY name"
|
||||
`shouldBe` Right [[VStr "Alice"], [VStr "Carol"]]
|
||||
values "SELECT name FROM users WHERE age <= 20" `shouldBe` Right [[VStr "Bob"]]
|
||||
|
||||
it "keeps comparisons with NULL three-valued" $ do
|
||||
executeOperator OpGtE [VNull, VInt 1] `shouldBe` Right VNull
|
||||
executeOperator OpLtE [VInt 1, VNull] `shouldBe` Right VNull
|
||||
executeOperator OpNe [VNull, VInt 1] `shouldBe` Right VNull
|
||||
executeOperator OpGtE [VInt 1, VFloat 1.0] `shouldBe` Right (VBool True)
|
||||
executeOperator OpNe [VInt 1, VFloat 1.0] `shouldBe` Right (VBool False)
|
||||
|
||||
it "rewrites >= and <= into an inclusive index range" $ do
|
||||
let stat rows distinct indexes = TableMeta rows [(c, d, False) | (c, d) <- distinct] indexes
|
||||
dbWith m = [("users", Table "users" [("id", TInt), ("name", TStr)] [] (Just m))]
|
||||
indexed = dbWith (stat 10000 [("id", 10000)] ["id"])
|
||||
low = Filter (GtE (Col "id") (LitInt 2)) (Scan Nothing "users" Nothing)
|
||||
both = Filter (And (GtE (Col "id") (LitInt 1)) (LtE (Col "id") (LitInt 3))) (Scan Nothing "users" Nothing)
|
||||
halfOpenTop = Filter (And (GtE (Col "id") (LitInt 1)) (Lt (Col "id") (LitInt 3))) (Scan Nothing "users" Nothing)
|
||||
halfOpenBottom = Filter (And (Gt (Col "id") (LitInt 1)) (LtE (Col "id") (LitInt 3))) (Scan Nothing "users" Nothing)
|
||||
optimize indexed low `shouldBe` Range Nothing "users" "id" (Just (VInt 2, True)) Nothing
|
||||
optimize indexed both
|
||||
`shouldBe` Range Nothing "users" "id" (Just (VInt 1, True)) (Just (VInt 3, True))
|
||||
-- 开区间那一端标记取反
|
||||
optimize indexed halfOpenTop
|
||||
`shouldBe` Range Nothing "users" "id" (Just (VInt 1, True)) (Just (VInt 3, False))
|
||||
optimize indexed halfOpenBottom
|
||||
`shouldBe` Range Nothing "users" "id" (Just (VInt 1, False)) (Just (VInt 3, True))
|
||||
optimize indexed (Filter (LtE (Col "id") (LitInt 9)) (Scan Nothing "users" Nothing))
|
||||
`shouldBe` Range Nothing "users" "id" Nothing (Just (VInt 9, True))
|
||||
-- 统计说索引不划算时保持原样,交给全表扫
|
||||
optimize (dbWith (stat 10000 [("id", 10000)] [])) low `shouldBe` low
|
||||
|
||||
it "uses an inclusive range on the real storage" $ do
|
||||
withTestSession $ \srv -> withServerEnv srv $ do
|
||||
_ <- runIPCStorage (runStatementM (CreateTable "rng_t" [("id", TInt), ("code", TInt)]))
|
||||
_ <-
|
||||
runIPCStorage
|
||||
( runStatementM
|
||||
( Insert
|
||||
"rng_t"
|
||||
["id", "code"]
|
||||
[ [LitInt 1, LitInt 500]
|
||||
, [LitInt 2, LitInt 501]
|
||||
, [LitInt 3, LitInt 502]
|
||||
, [LitInt 4, LitInt 503]
|
||||
]
|
||||
)
|
||||
)
|
||||
_ <- runIPCStorage (runStatementM (CreateIndex "rng_t" "code"))
|
||||
-- ids:按条件取 id,只比较值
|
||||
let ids cond =
|
||||
fmap (map (map snd))
|
||||
<$> runIPCStorage (runStatementM (makeSelect ["id"] "rng_t" (Just cond)))
|
||||
ids (And (GtE (Col "code") (LitInt 501)) (LtE (Col "code") (LitInt 502)))
|
||||
`shouldReturn` Right [[VInt 2], [VInt 3]]
|
||||
-- 闭端含边界、开端不含边界
|
||||
ids (And (Gt (Col "code") (LitInt 501)) (LtE (Col "code") (LitInt 502)))
|
||||
`shouldReturn` Right [[VInt 3]]
|
||||
ids (And (GtE (Col "code") (LitInt 501)) (Lt (Col "code") (LitInt 502)))
|
||||
`shouldReturn` Right [[VInt 2]]
|
||||
ids (And (Gt (Col "code") (LitInt 501)) (Lt (Col "code") (LitInt 502)))
|
||||
`shouldReturn` Right []
|
||||
|
||||
describe "ChuSQL.Core.Engine (SELECT)" $ do
|
||||
-- sql:在默认库上跑 SQL 取结果行
|
||||
let sql input = parseStatement input >>= rowsOf . runStatement testDB
|
||||
@@ -2660,6 +2744,7 @@ planTables op = case op of
|
||||
Sort _ x -> planTables x
|
||||
Limit _ x -> planTables x
|
||||
Join _ l r _ -> planTables l ++ planTables r
|
||||
Derived _ x -> planTables x
|
||||
Unit -> []
|
||||
|
||||
-- | 计划里每个 Join 节点是不是 LEFT JOIN(先序)
|
||||
@@ -2671,6 +2756,7 @@ leftJoinKinds (Aggregate _ _ x) = leftJoinKinds x
|
||||
leftJoinKinds (Filter _ x) = leftJoinKinds x
|
||||
leftJoinKinds (Sort _ x) = leftJoinKinds x
|
||||
leftJoinKinds (Limit _ x) = leftJoinKinds x
|
||||
leftJoinKinds (Derived _ x) = leftJoinKinds x
|
||||
leftJoinKinds _ = []
|
||||
|
||||
-- | 取计划里第一个过滤条件
|
||||
@@ -2688,6 +2774,7 @@ firstFilterCond (Join _ l r _) = case firstFilterCond l of
|
||||
firstFilterCond (Scan _ _ _) = Nothing
|
||||
firstFilterCond (Lookup _ _ _ _) = Nothing
|
||||
firstFilterCond (Range _ _ _ _ _) = Nothing
|
||||
firstFilterCond (Derived _ x) = firstFilterCond x
|
||||
|
||||
-- | 判断计划里是否还有过滤节点
|
||||
anyFilter :: RelOp -> Bool
|
||||
@@ -2702,6 +2789,7 @@ anyFilter (Join _ l r _) = anyFilter l || anyFilter r
|
||||
anyFilter (Scan _ _ _) = False
|
||||
anyFilter (Lookup _ _ _ _) = False
|
||||
anyFilter (Range _ _ _ _ _) = False
|
||||
anyFilter (Derived _ x) = anyFilter x
|
||||
|
||||
-- | 去掉计划里的投影节点
|
||||
stripProjects :: RelOp -> RelOp
|
||||
@@ -2716,6 +2804,7 @@ stripProjects (Join k l r c) = Join k (stripProjects l) (stripProjects r) c
|
||||
stripProjects (Scan a t _) = Scan a t Nothing
|
||||
stripProjects (Lookup a t c k) = Lookup a t c k
|
||||
stripProjects (Range a t c lo hi) = Range a t c lo hi
|
||||
stripProjects (Derived a x) = Derived a (stripProjects x)
|
||||
|
||||
-- | 解析并做投影下推,返回计划
|
||||
projectedPlan :: String -> Either String RelOp
|
||||
|
||||
Reference in new issue
Block a user