2026-09-19 19:22:50 +08:00
|
|
|
|
{-# LANGUAGE ImportQualifiedPost #-}
|
|
|
|
|
|
|
2026-10-01 20:48:38 +08:00
|
|
|
|
module ChuSQL.Syntax.Parser (parseStatement, parseExpression) where
|
2026-09-19 19:22:50 +08:00
|
|
|
|
|
2026-10-01 20:48:38 +08:00
|
|
|
|
import ChuSQL.Model (Column (..), ColumnType (..), Value (..), plainColumn)
|
2026-09-22 21:26:54 +08:00
|
|
|
|
import ChuSQL.Syntax.AST
|
2026-10-01 20:48:38 +08:00
|
|
|
|
import Control.Monad (void)
|
2026-09-19 19:22:50 +08:00
|
|
|
|
import Control.Monad.Combinators.Expr (Operator (..), makeExprParser)
|
2026-10-01 20:48:38 +08:00
|
|
|
|
import Data.Char (isAlpha, isAlphaNum, isHexDigit, isSpace, toLower, toUpper)
|
2026-09-19 22:42:22 +08:00
|
|
|
|
import Data.List (intercalate)
|
2026-09-19 19:22:50 +08:00
|
|
|
|
import Data.Maybe (fromMaybe)
|
|
|
|
|
|
import Data.Void (Void)
|
|
|
|
|
|
import Text.Megaparsec
|
|
|
|
|
|
import Text.Megaparsec.Char
|
|
|
|
|
|
import Text.Megaparsec.Char.Lexer qualified as Lxr
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- SQL 解析:词法 + 语法,把文本变成语句树。
|
2026-09-24 15:37:55 +08:00
|
|
|
|
|
2026-09-19 19:22:50 +08:00
|
|
|
|
type Parser = Parsec Void String
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 跳过空白
|
2026-09-19 19:22:50 +08:00
|
|
|
|
sc :: Parser ()
|
2026-10-01 20:48:38 +08:00
|
|
|
|
sc = Lxr.space space1 (Lxr.skipLineComment "--") (Lxr.skipBlockComment "/*" "*/")
|
2026-09-19 19:22:50 +08:00
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 词后面吃掉空白
|
2026-09-19 19:22:50 +08:00
|
|
|
|
lexeme :: Parser a -> Parser a
|
|
|
|
|
|
lexeme = Lxr.lexeme sc
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 读一个符号
|
2026-09-19 19:22:50 +08:00
|
|
|
|
symbol :: String -> Parser String
|
|
|
|
|
|
symbol = Lxr.symbol sc
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 读关键字(不分大小写)
|
2026-09-19 19:22:50 +08:00
|
|
|
|
keyword :: String -> Parser ()
|
|
|
|
|
|
keyword k = lexeme $ try $ do
|
|
|
|
|
|
_ <- string' k
|
|
|
|
|
|
notFollowedBy (satisfy isIdentChar)
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 读一个名字
|
2026-09-19 19:22:50 +08:00
|
|
|
|
identifier :: Parser String
|
|
|
|
|
|
identifier = lexeme $ do
|
|
|
|
|
|
first <- satisfy (\ch -> isAlpha ch || ch == '_')
|
|
|
|
|
|
rest <- many (satisfy isIdentChar)
|
|
|
|
|
|
return (first : rest)
|
|
|
|
|
|
|
2026-10-01 20:48:38 +08:00
|
|
|
|
-- | 账号名:裸名字或单引号字符串(账号名允许点与短横线,字符串形式才写得出来)
|
|
|
|
|
|
userName :: Parser String
|
|
|
|
|
|
userName = identifier <|> stringLit
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 名字里允许的字符
|
2026-09-19 19:22:50 +08:00
|
|
|
|
isIdentChar :: Char -> Bool
|
|
|
|
|
|
isIdentChar ch = isAlphaNum ch || ch == '_'
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 读一个整数
|
2026-09-19 19:22:50 +08:00
|
|
|
|
integer :: Parser Int
|
|
|
|
|
|
integer = lexeme Lxr.decimal
|
|
|
|
|
|
|
2026-10-01 20:48:38 +08:00
|
|
|
|
-- | 读一个浮点数(必须有小数点或指数)
|
|
|
|
|
|
floatLit :: Parser Double
|
|
|
|
|
|
floatLit = lexeme Lxr.float
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 读一个字符串字面量
|
2026-09-19 19:22:50 +08:00
|
|
|
|
stringLit :: Parser String
|
|
|
|
|
|
stringLit = lexeme $ do
|
|
|
|
|
|
_ <- char '\''
|
|
|
|
|
|
s <- many stringChar
|
|
|
|
|
|
_ <- char '\''
|
|
|
|
|
|
return s
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 字符串里的一个字符
|
2026-09-19 19:22:50 +08:00
|
|
|
|
stringChar :: Parser Char
|
|
|
|
|
|
stringChar =
|
|
|
|
|
|
choice
|
|
|
|
|
|
[ '\'' <$ try (string "''")
|
|
|
|
|
|
, satisfy (/= '\'')
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2026-10-01 20:48:38 +08:00
|
|
|
|
-- | X'ABCD' 形式的二进制字面量,统一存成大写十六进制文本
|
|
|
|
|
|
blobLit :: Parser String
|
|
|
|
|
|
blobLit = lexeme $ do
|
|
|
|
|
|
_ <- char 'x' <|> char 'X'
|
|
|
|
|
|
_ <- char '\''
|
|
|
|
|
|
s <- many (satisfy isHexDigit)
|
|
|
|
|
|
_ <- char '\''
|
|
|
|
|
|
return (map toUpper s)
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 升序或降序
|
2026-09-19 19:22:50 +08:00
|
|
|
|
sortDir :: Parser SortDir
|
|
|
|
|
|
sortDir = (Desc <$ keyword "desc") <|> (Asc <$ keyword "asc") <|> pure Asc
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | LIMIT 后面的整数
|
2026-09-19 19:22:50 +08:00
|
|
|
|
limitClause :: Parser Int
|
|
|
|
|
|
limitClause = do
|
|
|
|
|
|
keyword "limit"
|
|
|
|
|
|
integer
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 能带表名的列名
|
2026-09-19 22:42:22 +08:00
|
|
|
|
qualifiedName :: Parser String
|
|
|
|
|
|
qualifiedName = do
|
|
|
|
|
|
first <- identifier
|
|
|
|
|
|
rest <- many (symbol "." *> identifier)
|
|
|
|
|
|
return (intercalate "." (first : rest))
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 读一个表达式
|
2026-09-19 19:22:50 +08:00
|
|
|
|
expr :: Parser Expr
|
|
|
|
|
|
expr = makeExprParser atom operatorTable
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 运算符优先级(从紧到松)
|
2026-09-19 19:22:50 +08:00
|
|
|
|
operatorTable :: [[Operator Parser Expr]]
|
|
|
|
|
|
operatorTable =
|
2026-10-01 20:48:38 +08:00
|
|
|
|
[ [Prefix (Neg <$ symbol "-")]
|
|
|
|
|
|
, [InfixL (Mul <$ symbol "*"), InfixL (Div <$ symbol "/")]
|
|
|
|
|
|
, [InfixL (Add <$ symbol "+"), InfixL (Sub <$ symbol "-")]
|
|
|
|
|
|
, [ InfixN (Gt <$ symbol ">")
|
2026-09-19 19:22:50 +08:00
|
|
|
|
, InfixN (Lt <$ symbol "<")
|
|
|
|
|
|
, InfixN (Eq <$ symbol "=")
|
|
|
|
|
|
]
|
2026-10-01 20:48:38 +08:00
|
|
|
|
, [ Postfix inPredicate
|
|
|
|
|
|
, Postfix (IsNotNull <$ try (keyword "is" *> keyword "not" *> keyword "null"))
|
|
|
|
|
|
, Postfix (IsNull <$ (keyword "is" *> keyword "null"))
|
|
|
|
|
|
]
|
2026-09-19 19:22:50 +08:00
|
|
|
|
, [InfixL (And <$ keyword "AND")]
|
2026-09-25 17:41:51 +08:00
|
|
|
|
, [InfixL (Or <$ keyword "OR")]
|
2026-09-19 19:22:50 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 最小的表达式单位
|
2026-09-19 19:22:50 +08:00
|
|
|
|
atom :: Parser Expr
|
|
|
|
|
|
atom =
|
|
|
|
|
|
choice
|
2026-10-01 20:48:38 +08:00
|
|
|
|
[ LitNull <$ keyword "null"
|
|
|
|
|
|
, LitBool True <$ keyword "TRUE"
|
2026-09-25 17:41:51 +08:00
|
|
|
|
, LitBool False <$ keyword "FALSE"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
, try (LitDate <$> (keyword "date" *> stringLit))
|
|
|
|
|
|
, try (LitTimestamp <$> (keyword "timestamp" *> stringLit))
|
|
|
|
|
|
, try (LitBlob <$> blobLit)
|
|
|
|
|
|
, LitFloat <$> try floatLit
|
2026-09-25 17:41:51 +08:00
|
|
|
|
, LitInt <$> integer
|
2026-09-19 19:22:50 +08:00
|
|
|
|
, LitStr <$> stringLit
|
2026-10-01 20:48:38 +08:00
|
|
|
|
, try aggregateCall
|
|
|
|
|
|
, try existsPredicate
|
2026-09-19 22:42:22 +08:00
|
|
|
|
, Col <$> qualifiedName
|
2026-10-01 20:48:38 +08:00
|
|
|
|
, try scalarSubquery
|
2026-09-19 19:22:50 +08:00
|
|
|
|
, between (symbol "(") (symbol ")") expr
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2026-10-01 20:48:38 +08:00
|
|
|
|
-- | [NOT] EXISTS (SELECT ...)
|
|
|
|
|
|
existsPredicate :: Parser Expr
|
|
|
|
|
|
existsPredicate = do
|
|
|
|
|
|
negated <- (True <$ try (keyword "not" *> keyword "exists")) <|> (False <$ keyword "exists")
|
|
|
|
|
|
sq <- between (symbol "(") (symbol ")") subquerySelect
|
|
|
|
|
|
return (ExistsSub sq negated)
|
|
|
|
|
|
|
|
|
|
|
|
-- | 标量子查询:(SELECT ...)
|
|
|
|
|
|
scalarSubquery :: Parser Expr
|
|
|
|
|
|
scalarSubquery = ScalarSub <$> between (symbol "(") (symbol ")") subquerySelect
|
|
|
|
|
|
|
|
|
|
|
|
-- | [NOT] IN (SELECT ...) 或 [NOT] IN (值清单)
|
|
|
|
|
|
inPredicate :: Parser (Expr -> Expr)
|
|
|
|
|
|
inPredicate = do
|
|
|
|
|
|
negated <- (True <$ try (keyword "not" *> keyword "in")) <|> (False <$ keyword "in")
|
|
|
|
|
|
body <-
|
|
|
|
|
|
between (symbol "(") (symbol ")") $
|
|
|
|
|
|
choice
|
|
|
|
|
|
[ Left <$> try subquerySelect
|
|
|
|
|
|
, Right <$> sepBy1 expr (symbol ",")
|
|
|
|
|
|
]
|
|
|
|
|
|
return $ \e -> case body of
|
|
|
|
|
|
Left sq -> InSub e sq negated
|
|
|
|
|
|
Right es -> InList e es negated
|
|
|
|
|
|
|
|
|
|
|
|
-- | 括号里的子查询;对外层列的引用留给语义检查填
|
|
|
|
|
|
subquerySelect :: Parser Subquery
|
|
|
|
|
|
subquerySelect = Subquery <$> selectStatement <*> pure []
|
|
|
|
|
|
|
|
|
|
|
|
-- | 聚合调用:COUNT(*) / COUNT(x) / SUM(x) / AVG(x) / MIN(x) / MAX(x)
|
|
|
|
|
|
aggregateCall :: Parser Expr
|
|
|
|
|
|
aggregateCall =
|
|
|
|
|
|
choice
|
|
|
|
|
|
[ CountAll <$ try (keyword "count" *> between (symbol "(") (symbol ")") (symbol "*"))
|
|
|
|
|
|
, CountOf <$> aggArg "count"
|
|
|
|
|
|
, SumOf <$> aggArg "sum"
|
|
|
|
|
|
, AvgOf <$> aggArg "avg"
|
|
|
|
|
|
, MinOf <$> aggArg "min"
|
|
|
|
|
|
, MaxOf <$> aggArg "max"
|
|
|
|
|
|
]
|
|
|
|
|
|
where
|
|
|
|
|
|
aggArg name = try (keyword name *> between (symbol "(") (symbol ")") expr)
|
|
|
|
|
|
|
|
|
|
|
|
-- | 只认字面量,给 DEFAULT 用
|
|
|
|
|
|
literalValue :: Parser Value
|
|
|
|
|
|
literalValue =
|
|
|
|
|
|
choice
|
|
|
|
|
|
[ VNull <$ keyword "null"
|
|
|
|
|
|
, VBool True <$ keyword "TRUE"
|
|
|
|
|
|
, VBool False <$ keyword "FALSE"
|
|
|
|
|
|
, try (negateLit <$> floatLit)
|
|
|
|
|
|
, VInt <$> integer
|
|
|
|
|
|
, VFloat <$> try floatLit
|
|
|
|
|
|
, VStr <$> stringLit
|
|
|
|
|
|
]
|
|
|
|
|
|
where
|
|
|
|
|
|
negateLit d = VFloat (negate d)
|
2026-09-19 19:22:50 +08:00
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 一个排序列
|
2026-09-19 19:22:50 +08:00
|
|
|
|
orderItem :: Parser (String, SortDir)
|
|
|
|
|
|
orderItem = do
|
2026-09-19 22:42:22 +08:00
|
|
|
|
col <- qualifiedName
|
2026-09-19 19:22:50 +08:00
|
|
|
|
dir <- sortDir
|
|
|
|
|
|
return (col, dir)
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | ORDER BY 的列表
|
2026-09-19 19:22:50 +08:00
|
|
|
|
orderByClause :: Parser [(String, SortDir)]
|
|
|
|
|
|
orderByClause = do
|
|
|
|
|
|
keyword "order"
|
|
|
|
|
|
keyword "by"
|
|
|
|
|
|
sepBy1 orderItem (symbol ",")
|
|
|
|
|
|
|
2026-10-01 20:48:38 +08:00
|
|
|
|
-- | GROUP BY 的列表
|
|
|
|
|
|
groupByClause :: Parser [String]
|
|
|
|
|
|
groupByClause = do
|
|
|
|
|
|
keyword "group"
|
|
|
|
|
|
keyword "by"
|
|
|
|
|
|
sepBy1 qualifiedName (symbol ",")
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | SET 里的一条赋值
|
2026-09-19 19:22:50 +08:00
|
|
|
|
assignment :: Parser (String, Expr)
|
|
|
|
|
|
assignment = do
|
|
|
|
|
|
col <- identifier
|
|
|
|
|
|
_ <- symbol "="
|
|
|
|
|
|
e <- expr
|
|
|
|
|
|
return (col, e)
|
|
|
|
|
|
|
2026-09-19 22:42:22 +08:00
|
|
|
|
reservedWords :: [String]
|
|
|
|
|
|
reservedWords =
|
|
|
|
|
|
[ "select"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
, "as"
|
2026-09-19 22:42:22 +08:00
|
|
|
|
, "from"
|
|
|
|
|
|
, "where"
|
|
|
|
|
|
, "order"
|
|
|
|
|
|
, "by"
|
|
|
|
|
|
, "limit"
|
|
|
|
|
|
, "join"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
, "left"
|
|
|
|
|
|
, "outer"
|
|
|
|
|
|
, "inner"
|
|
|
|
|
|
, "exists"
|
|
|
|
|
|
, "in"
|
2026-09-19 22:42:22 +08:00
|
|
|
|
, "on"
|
|
|
|
|
|
, "and"
|
|
|
|
|
|
, "or"
|
|
|
|
|
|
, "asc"
|
|
|
|
|
|
, "desc"
|
|
|
|
|
|
, "insert"
|
|
|
|
|
|
, "into"
|
|
|
|
|
|
, "values"
|
|
|
|
|
|
, "delete"
|
|
|
|
|
|
, "update"
|
|
|
|
|
|
, "set"
|
2026-09-24 21:28:49 +08:00
|
|
|
|
, "create"
|
|
|
|
|
|
, "table"
|
2026-09-25 14:21:55 +08:00
|
|
|
|
, "index"
|
|
|
|
|
|
, "drop"
|
2026-09-25 17:41:51 +08:00
|
|
|
|
, "alter"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
, "add"
|
|
|
|
|
|
, "rename"
|
|
|
|
|
|
, "to"
|
2026-09-25 17:41:51 +08:00
|
|
|
|
, "column"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
, "user"
|
|
|
|
|
|
, "identified"
|
|
|
|
|
|
, "null"
|
|
|
|
|
|
, "is"
|
|
|
|
|
|
, "not"
|
|
|
|
|
|
, "default"
|
|
|
|
|
|
, "check"
|
|
|
|
|
|
, "unique"
|
|
|
|
|
|
, "primary"
|
|
|
|
|
|
, "key"
|
|
|
|
|
|
, "auto_increment"
|
|
|
|
|
|
, "type"
|
|
|
|
|
|
, "group"
|
2026-09-19 22:42:22 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 别名(不许用保留字)
|
2026-09-19 22:42:22 +08:00
|
|
|
|
aliasName :: Parser String
|
|
|
|
|
|
aliasName = try $ do
|
|
|
|
|
|
name <- identifier
|
|
|
|
|
|
if map toLower name `elem` reservedWords then empty else return name
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 表名 + 可选别名
|
2026-09-19 22:42:22 +08:00
|
|
|
|
tableRef :: Parser (Maybe String, String)
|
|
|
|
|
|
tableRef = do
|
2026-10-01 20:48:38 +08:00
|
|
|
|
tbl <- tableName
|
|
|
|
|
|
mAlias <- optional ((keyword "as" *> aliasName) <|> aliasName)
|
2026-09-19 22:42:22 +08:00
|
|
|
|
return (mAlias, tbl)
|
|
|
|
|
|
|
2026-10-01 20:48:38 +08:00
|
|
|
|
-- | 连接类型:内连接 / 左外连接
|
|
|
|
|
|
joinKind :: Parser JoinKind
|
|
|
|
|
|
joinKind =
|
|
|
|
|
|
try (keyword "left" *> optional (keyword "outer") *> keyword "join" *> return LeftJoin)
|
|
|
|
|
|
<|> try (keyword "inner" *> keyword "join" *> return InnerJoin)
|
|
|
|
|
|
<|> (keyword "join" *> return InnerJoin)
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 一个 JOIN ... ON
|
2026-10-01 20:48:38 +08:00
|
|
|
|
joinClause :: Parser (JoinKind, Maybe String, String, Expr)
|
2026-09-19 22:42:22 +08:00
|
|
|
|
joinClause = do
|
2026-10-01 20:48:38 +08:00
|
|
|
|
kind <- joinKind
|
2026-09-19 22:42:22 +08:00
|
|
|
|
(mAlias, tbl) <- tableRef
|
|
|
|
|
|
keyword "on"
|
|
|
|
|
|
cond <- expr
|
2026-10-01 20:48:38 +08:00
|
|
|
|
return (kind, mAlias, tbl, cond)
|
2026-09-19 22:42:22 +08:00
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | FROM 子句(可含多个 JOIN)
|
2026-09-19 22:42:22 +08:00
|
|
|
|
fromClause :: Parser FromClause
|
|
|
|
|
|
fromClause = do
|
|
|
|
|
|
(mAlias, tbl) <- tableRef
|
|
|
|
|
|
joins <- many joinClause
|
2026-10-01 20:48:38 +08:00
|
|
|
|
return (foldl (\acc (k, a, t, c) -> FromJoin k acc a t c) (FromTable mAlias tbl) joins)
|
2026-09-19 22:42:22 +08:00
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | SELECT 的列清单
|
2026-10-01 20:48:38 +08:00
|
|
|
|
selectList :: Parser [(String, Expr)]
|
2026-09-19 19:22:50 +08:00
|
|
|
|
selectList =
|
|
|
|
|
|
choice
|
2026-10-01 20:48:38 +08:00
|
|
|
|
[ [("*", Col "*")] <$ symbol "*"
|
|
|
|
|
|
, sepBy1 selectItem (symbol ",")
|
2026-09-19 19:22:50 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
2026-10-01 20:48:38 +08:00
|
|
|
|
-- | 投影标签保留表达式原文
|
|
|
|
|
|
selectItem :: Parser (String, Expr)
|
|
|
|
|
|
selectItem = do
|
|
|
|
|
|
(source, e) <- match expr
|
|
|
|
|
|
pure (case e of Col c -> (c, e); _ -> (reverse (dropWhile isSpace (reverse source)), e))
|
2026-09-24 21:28:49 +08:00
|
|
|
|
|
|
|
|
|
|
-- | 读 CREATE TABLE
|
|
|
|
|
|
createTableStatement :: Parser Statement
|
|
|
|
|
|
createTableStatement = do
|
|
|
|
|
|
keyword "create"
|
|
|
|
|
|
keyword "table"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
name <- tableName
|
2026-09-24 21:28:49 +08:00
|
|
|
|
cols <- between (symbol "(") (symbol ")") (sepBy1 columnDef (symbol ","))
|
|
|
|
|
|
return (CreateTable name cols)
|
|
|
|
|
|
|
|
|
|
|
|
-- | 读 DROP TABLE
|
|
|
|
|
|
dropTableStatement :: Parser Statement
|
|
|
|
|
|
dropTableStatement = do
|
|
|
|
|
|
keyword "drop"
|
|
|
|
|
|
keyword "table"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
name <- tableName
|
2026-09-24 21:28:49 +08:00
|
|
|
|
return (DropTable name)
|
|
|
|
|
|
|
2026-09-25 17:41:51 +08:00
|
|
|
|
-- | 读 CREATE INDEX(列名即索引名)
|
2026-09-25 14:21:55 +08:00
|
|
|
|
createIndexStatement :: Parser Statement
|
|
|
|
|
|
createIndexStatement = do
|
|
|
|
|
|
keyword "create"
|
|
|
|
|
|
keyword "index"
|
|
|
|
|
|
keyword "on"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
tbl <- tableName
|
2026-09-25 14:21:55 +08:00
|
|
|
|
col <- between (symbol "(") (symbol ")") identifier
|
|
|
|
|
|
return (CreateIndex tbl col)
|
|
|
|
|
|
|
|
|
|
|
|
-- | 读 DROP INDEX
|
|
|
|
|
|
dropIndexStatement :: Parser Statement
|
|
|
|
|
|
dropIndexStatement = do
|
|
|
|
|
|
keyword "drop"
|
|
|
|
|
|
keyword "index"
|
|
|
|
|
|
keyword "on"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
tbl <- tableName
|
2026-09-25 14:21:55 +08:00
|
|
|
|
col <- between (symbol "(") (symbol ")") identifier
|
|
|
|
|
|
return (DropIndex tbl col)
|
|
|
|
|
|
|
2026-10-01 20:48:38 +08:00
|
|
|
|
-- | 读 ALTER TABLE:加列、删列、改名、改类型、改默认值、改可空
|
|
|
|
|
|
alterTableStatement :: Parser Statement
|
|
|
|
|
|
alterTableStatement = do
|
2026-09-25 17:41:51 +08:00
|
|
|
|
keyword "alter"
|
|
|
|
|
|
keyword "table"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
tbl <- tableName
|
|
|
|
|
|
choice
|
|
|
|
|
|
[ do
|
|
|
|
|
|
keyword "add"
|
|
|
|
|
|
void (optional (keyword "column"))
|
|
|
|
|
|
(name, col) <- columnDef
|
|
|
|
|
|
pure (AddColumn tbl (name, col))
|
|
|
|
|
|
, do
|
|
|
|
|
|
keyword "drop"
|
|
|
|
|
|
void (optional (keyword "column"))
|
|
|
|
|
|
DropColumn tbl <$> identifier
|
|
|
|
|
|
, do
|
|
|
|
|
|
keyword "rename"
|
|
|
|
|
|
void (optional (keyword "column"))
|
|
|
|
|
|
old <- identifier
|
|
|
|
|
|
keyword "to"
|
|
|
|
|
|
RenameColumn tbl old <$> identifier
|
|
|
|
|
|
, do
|
|
|
|
|
|
keyword "alter"
|
|
|
|
|
|
void (optional (keyword "column"))
|
|
|
|
|
|
col <- identifier
|
|
|
|
|
|
choice
|
|
|
|
|
|
[ AlterColumnType tbl col <$> (keyword "type" *> columnTypeP)
|
|
|
|
|
|
, try (AlterColumnNull tbl col False <$ (keyword "set" *> keyword "not" *> keyword "null"))
|
|
|
|
|
|
, try (AlterColumnNull tbl col True <$ (keyword "drop" *> keyword "not" *> keyword "null"))
|
|
|
|
|
|
, try (AlterColumnDefault tbl col . Just <$> (keyword "set" *> keyword "default" *> literalValue))
|
|
|
|
|
|
, AlterColumnDefault tbl col Nothing <$ (keyword "drop" *> keyword "default")
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
2026-09-25 17:41:51 +08:00
|
|
|
|
|
2026-10-01 20:48:38 +08:00
|
|
|
|
-- | 读 CREATE USER(口令是明文,交给账号服务加盐派生)
|
|
|
|
|
|
createUserStatement :: Parser Statement
|
|
|
|
|
|
createUserStatement = do
|
|
|
|
|
|
keyword "create"
|
|
|
|
|
|
keyword "user"
|
|
|
|
|
|
name <- userName
|
|
|
|
|
|
pw <- identifiedBy
|
|
|
|
|
|
return (CreateUser name pw)
|
|
|
|
|
|
|
|
|
|
|
|
-- | 读 IDENTIFIED BY '口令'
|
|
|
|
|
|
identifiedBy :: Parser String
|
|
|
|
|
|
identifiedBy = do
|
|
|
|
|
|
keyword "identified"
|
|
|
|
|
|
keyword "by"
|
|
|
|
|
|
stringLit
|
|
|
|
|
|
|
|
|
|
|
|
-- | 读 ALTER USER ... IDENTIFIED BY(改一个普通账号的口令)
|
|
|
|
|
|
alterUserStatement :: Parser Statement
|
|
|
|
|
|
alterUserStatement = do
|
|
|
|
|
|
keyword "alter"
|
|
|
|
|
|
keyword "user"
|
|
|
|
|
|
name <- userName
|
|
|
|
|
|
pw <- identifiedBy
|
|
|
|
|
|
return (AlterUser name pw)
|
|
|
|
|
|
|
|
|
|
|
|
-- | 读 DROP USER
|
|
|
|
|
|
dropUserStatement :: Parser Statement
|
|
|
|
|
|
dropUserStatement = do
|
|
|
|
|
|
keyword "drop"
|
|
|
|
|
|
keyword "user"
|
|
|
|
|
|
name <- userName
|
|
|
|
|
|
return (DropUser name)
|
|
|
|
|
|
|
|
|
|
|
|
-- | 读 CREATE ROLE
|
|
|
|
|
|
createRoleStatement :: Parser Statement
|
|
|
|
|
|
createRoleStatement = do
|
|
|
|
|
|
keyword "create"
|
|
|
|
|
|
keyword "role"
|
|
|
|
|
|
CreateRole <$> userName
|
|
|
|
|
|
|
|
|
|
|
|
-- | 读 DROP ROLE
|
|
|
|
|
|
dropRoleStatement :: Parser Statement
|
|
|
|
|
|
dropRoleStatement = do
|
|
|
|
|
|
keyword "drop"
|
|
|
|
|
|
keyword "role"
|
|
|
|
|
|
DropRole <$> userName
|
|
|
|
|
|
|
|
|
|
|
|
-- | 权限名,统一收成大写
|
|
|
|
|
|
privilegeName :: Parser String
|
|
|
|
|
|
privilegeName =
|
|
|
|
|
|
choice
|
|
|
|
|
|
[ "SELECT" <$ keyword "select"
|
|
|
|
|
|
, "INSERT" <$ keyword "insert"
|
|
|
|
|
|
, "UPDATE" <$ keyword "update"
|
|
|
|
|
|
, "DELETE" <$ keyword "delete"
|
|
|
|
|
|
, "ALL" <$ keyword "all"
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
-- | ON 后面的授权对象:* 表示全部表,否则是一张表(可带库前缀)
|
|
|
|
|
|
grantObject :: Parser String
|
|
|
|
|
|
grantObject = ("*" <$ symbol "*") <|> tableName
|
|
|
|
|
|
|
|
|
|
|
|
-- | 读 GRANT:带 ON 的是给角色授权限,不带的是给用户加角色
|
|
|
|
|
|
grantStatement :: Parser Statement
|
|
|
|
|
|
grantStatement = do
|
|
|
|
|
|
keyword "grant"
|
|
|
|
|
|
choice
|
|
|
|
|
|
[ try $ do
|
|
|
|
|
|
privs <- sepBy1 privilegeName (symbol ",")
|
|
|
|
|
|
keyword "on"
|
|
|
|
|
|
obj <- grantObject
|
|
|
|
|
|
keyword "to"
|
|
|
|
|
|
GrantPrivileges privs obj <$> userName
|
|
|
|
|
|
, GrantRole <$> userName <*> (keyword "to" *> sepBy1 userName (symbol ","))
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
-- | 读 REVOKE:与 GRANT 对称
|
|
|
|
|
|
revokeStatement :: Parser Statement
|
|
|
|
|
|
revokeStatement = do
|
|
|
|
|
|
keyword "revoke"
|
|
|
|
|
|
choice
|
|
|
|
|
|
[ try $ do
|
|
|
|
|
|
privs <- sepBy1 privilegeName (symbol ",")
|
|
|
|
|
|
keyword "on"
|
|
|
|
|
|
obj <- grantObject
|
|
|
|
|
|
keyword "from"
|
|
|
|
|
|
RevokePrivileges privs obj <$> userName
|
|
|
|
|
|
, RevokeRole <$> userName <*> (keyword "from" *> sepBy1 userName (symbol ","))
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
-- | 建表时的一列:类型后面跟若干约束
|
2026-09-24 21:28:49 +08:00
|
|
|
|
columnDef :: Parser (String, Column)
|
|
|
|
|
|
columnDef = do
|
|
|
|
|
|
name <- identifier
|
2026-10-01 20:48:38 +08:00
|
|
|
|
ty <- columnTypeP
|
|
|
|
|
|
mods <- many columnConstraint
|
|
|
|
|
|
return (name, foldl (\c f -> f c) (plainColumn ty) mods)
|
|
|
|
|
|
|
|
|
|
|
|
-- | 一条列约束
|
|
|
|
|
|
columnConstraint :: Parser (Column -> Column)
|
|
|
|
|
|
columnConstraint =
|
|
|
|
|
|
choice
|
|
|
|
|
|
[ try (keyword "not" *> keyword "null" *> pure (\c -> c{columnNullable = False}))
|
|
|
|
|
|
, keyword "null" *> pure (\c -> c{columnNullable = True})
|
|
|
|
|
|
, keyword "auto_increment" *> pure (\c -> c{columnAutoIncrement = True, columnNullable = False})
|
|
|
|
|
|
, try (keyword "primary" *> keyword "key" *> pure (\c -> c{columnPrimaryKey = True, columnUnique = True, columnNullable = False}))
|
|
|
|
|
|
, keyword "unique" *> pure (\c -> c{columnUnique = True})
|
|
|
|
|
|
, keyword "default" *> ((\v c -> c{columnDefault = Just v}) <$> literalValue)
|
|
|
|
|
|
, keyword "check" *> ((\s c -> c{columnCheck = Just s}) <$> checkText)
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
-- | CHECK 后面括号里的表达式原文,交给执行期再解析
|
|
|
|
|
|
checkText :: Parser String
|
|
|
|
|
|
checkText = between (symbol "(") (symbol ")") (match expr) >>= \(source, _) -> pure source
|
|
|
|
|
|
|
|
|
|
|
|
-- | 括号里的长度参数
|
|
|
|
|
|
sizeParen :: Parser Int
|
|
|
|
|
|
sizeParen = try (between (symbol "(") (symbol ")") integer)
|
2026-09-24 21:28:49 +08:00
|
|
|
|
|
|
|
|
|
|
-- | 列类型关键字
|
2026-10-01 20:48:38 +08:00
|
|
|
|
columnTypeP :: Parser ColumnType
|
|
|
|
|
|
columnTypeP =
|
|
|
|
|
|
choice
|
|
|
|
|
|
[ keyword "integer" >> pure CInt
|
|
|
|
|
|
, keyword "int" >> pure CInt
|
|
|
|
|
|
, keyword "bigint" >> pure CBigInt
|
|
|
|
|
|
, keyword "smallint" >> pure CSmallInt
|
|
|
|
|
|
, (keyword "varchar" >>) (maybe CStr CVarchar <$> optional sizeParen)
|
|
|
|
|
|
, (keyword "char" >>) (maybe (CChar 1) CChar <$> optional sizeParen)
|
|
|
|
|
|
, keyword "text" >> pure CStr
|
|
|
|
|
|
, keyword "str" >> pure CStr
|
|
|
|
|
|
, keyword "boolean" >> pure CBool
|
|
|
|
|
|
, keyword "bool" >> pure CBool
|
|
|
|
|
|
, keyword "float" >> pure CFloat
|
|
|
|
|
|
, keyword "real" >> pure CFloat
|
|
|
|
|
|
, try (keyword "double" *> keyword "precision" *> pure CDouble)
|
|
|
|
|
|
, keyword "double" >> pure CDouble
|
|
|
|
|
|
, (keyword "decimal" >>) decimalType
|
|
|
|
|
|
, (keyword "numeric" >>) decimalType
|
|
|
|
|
|
, keyword "date" >> pure CDate
|
|
|
|
|
|
, keyword "timestamp" >> pure CTimestamp
|
|
|
|
|
|
, keyword "blob" >> pure CBlob
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
-- | DECIMAL 的精度与小数位
|
|
|
|
|
|
decimalType :: Parser ColumnType
|
|
|
|
|
|
decimalType = do
|
|
|
|
|
|
args <- optional (try (between (symbol "(") (symbol ")") decimalArgs))
|
|
|
|
|
|
pure $ case args of
|
|
|
|
|
|
Nothing -> CDecimal 10 0
|
|
|
|
|
|
Just (p, s) -> CDecimal p s
|
|
|
|
|
|
|
|
|
|
|
|
decimalArgs :: Parser (Int, Int)
|
|
|
|
|
|
decimalArgs = do
|
|
|
|
|
|
p <- integer
|
|
|
|
|
|
s <- fromMaybe 0 <$> optional (symbol "," *> integer)
|
|
|
|
|
|
pure (p, s)
|
2026-09-24 21:28:49 +08:00
|
|
|
|
|
|
|
|
|
|
-- | 读 SELECT
|
|
|
|
|
|
selectStatement :: Parser Statement
|
|
|
|
|
|
selectStatement = do
|
2026-09-19 22:42:22 +08:00
|
|
|
|
keyword "select"
|
2026-09-19 19:22:50 +08:00
|
|
|
|
cols <- selectList
|
2026-10-01 20:48:38 +08:00
|
|
|
|
fromC <- fromMaybe FromUnit <$> optional (keyword "from" *> fromClause)
|
2026-09-19 19:22:50 +08:00
|
|
|
|
mWhere <- optional (keyword "where" *> expr)
|
2026-10-01 20:48:38 +08:00
|
|
|
|
groupBy <- fromMaybe [] <$> optional groupByClause
|
2026-09-19 19:22:50 +08:00
|
|
|
|
orderBy <- fromMaybe [] <$> optional orderByClause
|
|
|
|
|
|
mLimit <- optional limitClause
|
2026-10-01 20:48:38 +08:00
|
|
|
|
pure $ if all (\(name, e) -> e == Col name) cols
|
|
|
|
|
|
then Select (map fst cols) fromC mWhere groupBy orderBy mLimit
|
|
|
|
|
|
else SelectExpr cols fromC mWhere groupBy orderBy mLimit
|
2026-09-19 19:22:50 +08:00
|
|
|
|
|
2026-09-25 17:41:51 +08:00
|
|
|
|
-- | 读 INSERT,可一次插多行
|
2026-09-24 21:28:49 +08:00
|
|
|
|
insertStatement :: Parser Statement
|
|
|
|
|
|
insertStatement = do
|
2026-09-19 19:22:50 +08:00
|
|
|
|
keyword "INSERT"
|
|
|
|
|
|
keyword "INTO"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
tbl <- tableName
|
2026-09-19 19:22:50 +08:00
|
|
|
|
cols <-
|
|
|
|
|
|
between
|
|
|
|
|
|
(symbol "(")
|
|
|
|
|
|
(symbol ")")
|
|
|
|
|
|
(sepBy1 identifier (symbol ","))
|
|
|
|
|
|
keyword "VALUES"
|
2026-09-25 14:21:55 +08:00
|
|
|
|
rows <- sepBy1 valueRow (symbol ",")
|
|
|
|
|
|
return (Insert tbl cols rows)
|
|
|
|
|
|
|
|
|
|
|
|
-- | 一行值:一对括号里的若干字面量
|
|
|
|
|
|
valueRow :: Parser [Expr]
|
2026-10-01 20:48:38 +08:00
|
|
|
|
valueRow = between (symbol "(") (symbol ")") (sepBy1 expr (symbol ","))
|
2026-09-19 19:22:50 +08:00
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 读 DELETE
|
|
|
|
|
|
deleteStatement :: Parser Statement
|
|
|
|
|
|
deleteStatement = do
|
2026-09-19 19:22:50 +08:00
|
|
|
|
keyword "DELETE"
|
|
|
|
|
|
keyword "FROM"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
tbl <- tableName
|
2026-09-19 19:22:50 +08:00
|
|
|
|
mWhere <- optional (keyword "WHERE" *> expr)
|
|
|
|
|
|
return (Delete tbl mWhere)
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 读 UPDATE
|
|
|
|
|
|
updateStatement :: Parser Statement
|
|
|
|
|
|
updateStatement = do
|
2026-09-19 19:22:50 +08:00
|
|
|
|
keyword "update"
|
2026-10-01 20:48:38 +08:00
|
|
|
|
tbl <- tableName
|
2026-09-19 19:22:50 +08:00
|
|
|
|
keyword "set"
|
|
|
|
|
|
assigns <- sepBy1 assignment (symbol ",")
|
|
|
|
|
|
mWhere <- optional (keyword "where" *> expr)
|
|
|
|
|
|
return (Update tbl assigns mWhere)
|
|
|
|
|
|
|
2026-09-24 21:28:49 +08:00
|
|
|
|
-- | 解析总入口
|
|
|
|
|
|
parseStatement :: String -> Either String Statement
|
|
|
|
|
|
parseStatement input =
|
|
|
|
|
|
case runParser (sc *> statementP <* sc <* eof) "<query>" input of
|
2026-09-19 19:22:50 +08:00
|
|
|
|
Left err -> Left (errorBundlePretty err)
|
|
|
|
|
|
Right q -> Right q
|
2026-09-24 21:28:49 +08:00
|
|
|
|
where
|
|
|
|
|
|
statementP =
|
|
|
|
|
|
selectStatement
|
2026-10-01 20:48:38 +08:00
|
|
|
|
<|> try (CreateDatabase <$> (keyword "create" *> keyword "database" *> identifier))
|
|
|
|
|
|
<|> try (DropDatabase <$> (keyword "drop" *> keyword "database" *> identifier))
|
|
|
|
|
|
<|> (UseDatabase <$> (keyword "use" *> identifier))
|
|
|
|
|
|
<|> (ShowDatabases <$ (keyword "show" *> keyword "databases"))
|
2026-09-24 21:28:49 +08:00
|
|
|
|
<|> insertStatement
|
|
|
|
|
|
<|> deleteStatement
|
|
|
|
|
|
<|> updateStatement
|
2026-10-01 20:48:38 +08:00
|
|
|
|
<|> try createUserStatement
|
|
|
|
|
|
<|> try alterUserStatement
|
|
|
|
|
|
<|> try dropUserStatement
|
|
|
|
|
|
<|> try createRoleStatement
|
|
|
|
|
|
<|> try dropRoleStatement
|
|
|
|
|
|
<|> try grantStatement
|
|
|
|
|
|
<|> try revokeStatement
|
2026-09-25 14:21:55 +08:00
|
|
|
|
<|> try createIndexStatement
|
|
|
|
|
|
<|> try createTableStatement
|
|
|
|
|
|
<|> try dropIndexStatement
|
2026-10-01 20:48:38 +08:00
|
|
|
|
<|> try alterTableStatement
|
2026-09-24 21:28:49 +08:00
|
|
|
|
<|> dropTableStatement
|
2026-10-01 20:48:38 +08:00
|
|
|
|
|
|
|
|
|
|
-- | 解析一个裸表达式(CHECK 约束原文用)
|
|
|
|
|
|
parseExpression :: String -> Either String Expr
|
|
|
|
|
|
parseExpression input =
|
|
|
|
|
|
case runParser (sc *> expr <* sc <* eof) "<expr>" input of
|
|
|
|
|
|
Left err -> Left (errorBundlePretty err)
|
|
|
|
|
|
Right e -> Right e
|
|
|
|
|
|
|
|
|
|
|
|
-- | 表名至多有一个库前缀。
|
|
|
|
|
|
tableName :: Parser String
|
|
|
|
|
|
tableName = do
|
|
|
|
|
|
first <- identifier
|
|
|
|
|
|
databaseTable <- optional (symbol "." *> identifier)
|
|
|
|
|
|
pure (maybe first (\t -> map toLower first ++ "." ++ t) databaseTable)
|