Remove isnot and notin operators and refactor
This commit is contained in:
+10
-10
@@ -78,7 +78,7 @@ pFieldForest :: Parser [Tree SelectItem]
|
||||
pFieldForest = pFieldTree `sepBy1` lexeme (char ',')
|
||||
|
||||
pFieldTree :: Parser (Tree SelectItem)
|
||||
pFieldTree = try (Node <$> pRelationSelect <*> between (char '{') (char '}') pFieldForest)
|
||||
pFieldTree = try (Node <$> pRelationSelect <*> between (char '{') (char '}') pFieldForest) -- TODO: "{}" deprecated
|
||||
<|> try (Node <$> pRelationSelect <*> between (char '(') (char ')') pFieldForest)
|
||||
<|> Node <$> pFieldSelect <*> pure []
|
||||
|
||||
@@ -130,14 +130,13 @@ pFieldSelect = lexeme $
|
||||
s <- pStar
|
||||
return ((s, Nothing), Nothing, Nothing, Nothing)
|
||||
|
||||
pOpExpr :: Parser Text -> Parser [Text] -> Parser OpExpr
|
||||
pOpExpr :: Parser SingleVal -> Parser ListVal -> Parser OpExpr
|
||||
pOpExpr pSVal pLVal = try ( string "not" *> pDelimiter *> (OpExpr True <$> pOperation)) <|> OpExpr False <$> pOperation
|
||||
where
|
||||
pOperation :: Parser Operation
|
||||
pOperation =
|
||||
Op . toS <$> foldl1 (<|>) (try . ((<* pDelimiter) . string) . toS <$> M.keys ops) <*> pSVal
|
||||
<|> In . toS <$> (string "in" <* pDelimiter) <*> pLVal
|
||||
<|> In . toS <$> (string "notin" <* pDelimiter) <*> pLVal
|
||||
<|> In <$> (string "in" *> pDelimiter *> pLVal)
|
||||
<|> pFts
|
||||
<?> "operator (eq, gt, ...)"
|
||||
pFts = do
|
||||
@@ -149,14 +148,14 @@ pOpExpr pSVal pLVal = try ( string "not" *> pDelimiter *> (OpExpr True <$> pOper
|
||||
<|> try (string "fts" *> pDelimiter) *> pure Nothing
|
||||
<|> try (string "@@" *> pDelimiter) *> pure Nothing -- TODO: '@@' deprecated
|
||||
Fts mode (toS <$> lang) <$> pSVal
|
||||
ops = M.filterWithKey (const . flip notElem ["in", "notin", "fts", "@@"]) operators -- TODO: '@@' deprecated
|
||||
ops = M.filterWithKey (const . flip notElem ["in", "fts", "@@"]) operators -- TODO: '@@' deprecated
|
||||
|
||||
pSingleVal :: Parser Text
|
||||
pSingleVal :: Parser SingleVal
|
||||
pSingleVal = toS <$> many anyChar
|
||||
|
||||
pListVal :: Parser [Text]
|
||||
pListVal :: Parser ListVal
|
||||
pListVal = try (lexeme (char '(') *> pListElement `sepBy1` char ',' <* lexeme (char ')'))
|
||||
<|> lexeme pListElement `sepBy1` char ','
|
||||
<|> lexeme pListElement `sepBy1` char ',' -- TODO: "in.3,4,5" deprecated, parens e.g. "in.(3,4,5)" should be used
|
||||
|
||||
pListElement :: Parser Text
|
||||
pListElement = try pQuotedValue <|> (toS <$> many (noneOf ",)"))
|
||||
@@ -201,9 +200,10 @@ pLogicTree = Stmnt <$> try pLogicFilter
|
||||
<|> string "or" *> pure Or
|
||||
<?> "logic operator (and, or)"
|
||||
|
||||
pLogicSingleVal :: Parser Text
|
||||
pLogicSingleVal :: Parser SingleVal
|
||||
pLogicSingleVal = try pQuotedValue <|> try pPgArray <|> (toS <$> many (noneOf ",)"))
|
||||
where
|
||||
-- TODO: "{}" deprecated, after removal pPgArray can be removed
|
||||
pPgArray :: Parser Text
|
||||
pPgArray = do
|
||||
a <- string "{"
|
||||
@@ -211,7 +211,7 @@ pLogicSingleVal = try pQuotedValue <|> try pPgArray <|> (toS <$> many (noneOf ",
|
||||
c <- string "}"
|
||||
toS <$> pure (a ++ b ++ c)
|
||||
|
||||
pLogicListVal :: Parser [Text]
|
||||
pLogicListVal :: Parser ListVal
|
||||
pLogicListVal = lexeme (char '(') *> pListElement `sepBy1` char ',' <* lexeme (char ')')
|
||||
|
||||
pLogicPath :: Parser (EmbedPath, Text)
|
||||
|
||||
@@ -413,15 +413,19 @@ pgFmtSelectItem table (f@(_, jp), Nothing, alias, _) = pgFmtField table f <> pgF
|
||||
pgFmtSelectItem table (f@(_, jp), Just cast, alias, _) = "CAST (" <> pgFmtField table f <> " AS " <> cast <> " )" <> pgFmtAs jp alias
|
||||
|
||||
pgFmtFilter :: QualifiedIdentifier -> Filter -> SqlFragment
|
||||
pgFmtFilter table (Filter fld (OpExpr hasNot_ oper)) = notOp <> " " <> case oper of
|
||||
pgFmtFilter table (Filter fld (OpExpr hasNot oper)) = notOp <> " " <> case oper of
|
||||
Op op val -> pgFmtFieldOp op <> " " <> case op of
|
||||
"like" -> unknownLiteral (T.map star val)
|
||||
"ilike" -> unknownLiteral (T.map star val)
|
||||
"is" -> whiteList val
|
||||
"isnot" -> whiteList val
|
||||
_ -> unknownLiteral val
|
||||
|
||||
In op vals -> pgFmtIn op vals -- in and notin
|
||||
In vals -> pgFmtField table fld <> " " <>
|
||||
let emptyValForIn = "= any('{}') " in -- Workaround because for postgresql "col IN ()" is invalid syntax, we instead do "col = any('{}')"
|
||||
case ((&&) (length vals == 1) . T.null) <$> headMay vals of
|
||||
Just False -> sqlOperator "in" <> "(" <> intercalate ", " (map unknownLiteral vals) <> ") "
|
||||
Just True -> emptyValForIn
|
||||
Nothing -> emptyValForIn
|
||||
|
||||
Fts mode lang val ->
|
||||
pgFmtFieldOp "fts" <> " " <> case mode of
|
||||
@@ -435,27 +439,17 @@ pgFmtFilter table (Filter fld (OpExpr hasNot_ oper)) = notOp <> " " <> case oper
|
||||
where
|
||||
pgFmtFieldOp op = pgFmtField table fld <> " " <> sqlOperator op
|
||||
sqlOperator o = HM.lookupDefault "=" o operators
|
||||
notOp = if hasNot_ then "NOT" else ""
|
||||
notOp = if hasNot then "NOT" else ""
|
||||
star c = if c == '*' then '%' else c
|
||||
unknownLiteral = (<> "::unknown ") . pgFmtLit
|
||||
whiteList :: Text -> SqlFragment
|
||||
whiteList v = fromMaybe
|
||||
(toS (pgFmtLit v) <> "::unknown ")
|
||||
(find ((==) . toLower $ v) ["null","true","false"])
|
||||
pgFmtIn :: Operator -> [Text] -> SqlFragment
|
||||
pgFmtIn op vals =
|
||||
-- Workaround because for postgresql "col IN ()" is invalid syntax, we instead do "col = any('{}')"
|
||||
let emptyValForIn o = (if "not" `isInfixOf` o then "NOT " else "") -- handle case of "notin" operator
|
||||
<> pgFmtField table fld <> " = any('{}') " in
|
||||
case T.null <$> headMay vals of
|
||||
Just isNull -> if isNull && length vals == 1
|
||||
then emptyValForIn op
|
||||
else pgFmtFieldOp op <> "(" <> intercalate ", " (map unknownLiteral vals) <> ") "
|
||||
Nothing -> emptyValForIn op
|
||||
|
||||
pgFmtLogicTree :: QualifiedIdentifier -> LogicTree -> SqlFragment
|
||||
pgFmtLogicTree qi (Expr hasNot_ op forest) = notOp <> " (" <> intercalate (" " <> show op <> " ") (pgFmtLogicTree qi <$> forest) <> ")"
|
||||
where notOp = if hasNot_ then "NOT" else ""
|
||||
pgFmtLogicTree qi (Expr hasNot op forest) = notOp <> " (" <> intercalate (" " <> show op <> " ") (pgFmtLogicTree qi <$> forest) <> ")"
|
||||
where notOp = if hasNot then "NOT" else ""
|
||||
pgFmtLogicTree qi (Stmnt flt) = pgFmtFilter qi flt
|
||||
|
||||
pgFmtJsonPath :: Maybe JsonPath -> SqlFragment
|
||||
|
||||
@@ -158,8 +158,6 @@ operators = M.fromList [
|
||||
("like", "LIKE"),
|
||||
("ilike", "ILIKE"),
|
||||
("in", "IN"),
|
||||
("notin", "NOT IN"),
|
||||
("isnot", "IS NOT"),
|
||||
("is", "IS"),
|
||||
("fts", "@@"),
|
||||
("cs", "@>"),
|
||||
@@ -176,13 +174,17 @@ operators = M.fromList [
|
||||
("<@", "<@")]
|
||||
|
||||
data OpExpr = OpExpr Bool Operation deriving (Eq, Show)
|
||||
data Operation = Op Operator Text |
|
||||
In Operator [Text] |
|
||||
Fts FtsMode (Maybe Language) Text |
|
||||
data Operation = Op Operator SingleVal |
|
||||
In ListVal |
|
||||
Fts FtsMode (Maybe Language) SingleVal |
|
||||
Join QualifiedIdentifier ForeignKey deriving (Eq, Show)
|
||||
|
||||
data FtsMode = Normal | Plain | Phrase deriving (Eq, Show)
|
||||
type Language = Text
|
||||
-- | Represents a single value in a filter, e.g. id=eq.singleval
|
||||
type SingleVal = Text
|
||||
-- | Represents a list value in a filter, e.g. id=in.(val1,val2,val3)
|
||||
type ListVal = [Text]
|
||||
|
||||
data LogicOperator = And | Or deriving Eq
|
||||
instance Show LogicOperator where
|
||||
|
||||
Reference in New Issue
Block a user