module Database.Sql.Presto.Parser where
import Database.Sql.Type
import Database.Sql.Position
import Database.Sql.Helpers
import Database.Sql.Info
import Database.Sql.Presto.Type
import Database.Sql.Presto.Scanner
import Database.Sql.Presto.Parser.Internal
import qualified Database.Sql.Presto.Parser.Token as Tok
import qualified Text.Parsec as P
import Text.Parsec ( chainl1, choice, many
, option, optional, optionMaybe
, sepBy, sepBy1, try, (<|>), (<?>))
import Data.Char (isDigit)
import Data.Foldable (fold)
import qualified Data.List as L
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.Maybe (catMaybes)
import Data.Monoid (Endo (..))
import Data.Semigroup (Semigroup (..), sconcat)
import Data.Set (Set)
import qualified Data.Set as S
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL
import Control.Arrow (first)
import Control.Monad (when)
import Control.Monad.Reader (runReader, local, asks)
statementParser :: Parser (PrestoStatement RawNames Range)
statementParser = do
maybeStmt <- optionMaybe $ choice
[ try $ PrestoStandardSqlStatement <$> statementP
, PrestoUnhandledStatement <$> explainP
, PrestoUnhandledStatement <$> showP
, PrestoUnhandledStatement <$> callP
, PrestoUnhandledStatement <$> describeP
]
case maybeStmt of
Just stmt -> terminator >> return stmt
Nothing -> PrestoStandardSqlStatement <$> emptyStatementP
where
terminator = (Tok.semicolonP <|> eof)
emptyStatementP = EmptyStmt <$> Tok.semicolonP
emptyParserScope :: ParserScope
emptyParserScope = ParserScope
{ selectTableAliases = Nothing }
parse :: Text -> Either P.ParseError (PrestoStatement RawNames Range)
parse text = runReader (P.runParserT statementParser 0 "-" . tokenize $ text) emptyParserScope
parseAll :: Text -> Either P.ParseError (PrestoStatement RawNames Range)
parseAll text = runReader (P.runParserT (statementParser <* P.eof) 0 "-" . tokenize $ text) emptyParserScope
parseMany :: Text -> Either P.ParseError [PrestoStatement RawNames Range]
parseMany text = runReader (P.runParserT (P.many1 statementParser) 0 "-" . tokenize $ text) emptyParserScope
parseManyAll :: Text -> Either P.ParseError [PrestoStatement RawNames Range]
parseManyAll text = runReader (P.runParserT (P.many1 statementParser <* P.eof) 0 "-" . tokenize $ text) emptyParserScope
parseManyEithers :: Text -> Either P.ParseError [Either (Unparsed Range) (PrestoStatement RawNames Range)]
parseManyEithers text = runReader (P.runParserT parser 0 "-" . tokenize $ text) emptyParserScope
where
parser = do
statements <- P.many1 $ P.setState 0 >> choice
[ try $ Right <$> statementParser
, try $ Left <$> do
ss <- many Tok.notSemicolonP
e <- Tok.semicolonP
pure $ case ss of
[] -> Unparsed e
s:_ -> Unparsed (s <> e)
]
locs <- many Tok.notSemicolonP
P.eof
pure $ case locs of
[] -> statements
s:es -> statements ++ [Left $ Unparsed $ sconcat (s:|es)]
statementP :: Parser (Statement Presto RawNames Range)
statementP = choice
[ QueryStmt <$> queryP
, DeleteStmt <$> deleteP
, do
_ <- try $ P.lookAhead dropViewPrefixP
DropViewStmt <$> dropViewP
, DropTableStmt <$> dropTableP
, GrantStmt <$> grantP
, RevokeStmt <$> revokeP
, InsertStmt <$> insertP
]
queryP :: Parser (Query RawNames Range)
queryP = do
with <- option id withP
queryNoWith <- queryNoWithP
return $ with queryNoWith
where
withP :: Parser (Query RawNames Range -> Query RawNames Range)
withP = do
r <- Tok.withP
withs <- cteP `sepBy1` Tok.commaP
return $ \ query ->
let r' = sconcat $ r :| getInfo query : map cteInfo withs
in QueryWith r' withs query
cteP :: Parser (CTE RawNames Range)
cteP = do
alias <- tableAliasP
columns <- option [] $ P.between Tok.openP Tok.closeP $ columnAliasP `sepBy1` Tok.commaP
_ <- Tok.asP
_ <- Tok.openP
query <- queryP
r' <- Tok.closeP
return $ CTE (getInfo alias <> r') alias columns query
queryNoWithP :: Parser (Query RawNames Range)
queryNoWithP = do
queryTerm <- queryTermP
order <- option id (orderWrapperP queryTerm)
limit <- option id limitP
return $ limit $ order queryTerm
where
queryTermP = (queryPrimaryP `chainl1` (exceptP <|> unionP))
`chainl1` intersectP
queryPrimaryP = choice
[ querySelectP
, P.between Tok.openP Tok.closeP queryNoWithP
]
exceptP = do
r <- Tok.exceptP
optional Tok.distinctP
return $ QueryExcept r Unused
unionP = do
r <- Tok.unionP
distinct <- option (Distinct True) distinctP
return $ QueryUnion r distinct Unused
intersectP = do
r <- Tok.intersectP
optional Tok.distinctP
return $ QueryIntersect r Unused
orderWrapperP query = do
let aliases = aliasesForOrders query
(r, orders) <- local (introduceAliases aliases) orderTopLevelP
return $ \ query' -> QueryOrder (getInfo query' <> r) orders query'
aliasesForOrders query = case query of
QuerySelect _ s -> tableAliases $ selectFrom s
_ -> S.empty
limitP = do
r <- Tok.limitP
choice
[ do
(NumericConstant r' num) <- numberConstantP
let limit = Limit (r <> r') num
return $ \ query -> QueryLimit (getInfo query <> r') limit query
, Tok.allP >> return id
]
querySelectP :: Parser (Query RawNames Range)
querySelectP = do
select <- selectP
return $ QuerySelect (selectInfo select) select
selectP :: Parser (Select RawNames Range)
selectP = do
r <- Tok.selectP
selectDistinct <- option notDistinct distinctP
aliases <- try selectScopeLookAhead
selectCols <- do
selections <- local (introduceAliases aliases) $ selectionP `countingSepBy1` Tok.commaP
let r' = foldl1 (<>) $ map getInfo selections
return $ SelectColumns r' selections
selectFrom <- optionMaybe fromP
selectWhere <- optionMaybe $ local (introduceAliases aliases) whereP
let selectTimeseries = Nothing
selectGroup <- optionMaybe $ local (introduceAliases aliases) groupP
selectHaving <- optionMaybe $ local (introduceAliases aliases) havingP
let selectNamedWindow = Nothing
Just selectInfo = sconcat $ Just r :|
[ Just $ getInfo selectCols
, getInfo <$> selectFrom
, getInfo <$> selectWhere
, getInfo <$> selectGroup
, getInfo <$> selectHaving
]
pure Select{..}
where
selectScopeLookAhead :: Parser (Set Text)
selectScopeLookAhead = P.lookAhead $ do
_ <- selectionP (1) `sepBy1` Tok.commaP
from <- optionMaybe fromP
return $ tableAliases from
distinctP :: Parser Distinct
distinctP = choice $
[ Tok.allP >> return (Distinct False)
, Tok.distinctP >> return (Distinct True)
]
tableAliases :: Maybe (SelectFrom RawNames Range) -> Set Text
tableAliases from =
let tablishes = case from of
Just (SelectFrom _ ts) -> ts
Nothing -> []
in L.foldl' S.union S.empty $ map tablishToTableAlias tablishes
where
tablishToTableAlias :: Tablish RawNames Range -> Set Text
tablishToTableAlias (TablishTable _ aliases tableName) = case aliases of
TablishAliasesNone -> tableNameToTableAlias tableName
TablishAliasesT (TableAlias _ name _) -> S.singleton name
TablishAliasesTC (TableAlias _ name _) _ -> S.singleton name
tablishToTableAlias (TablishSubQuery _ aliases _) = case aliases of
TablishAliasesNone -> S.empty
TablishAliasesT (TableAlias _ name _) -> S.singleton name
TablishAliasesTC (TableAlias _ name _) _ -> S.singleton name
tablishToTableAlias (TablishLateralView _ LateralView{..} _) = case lateralViewAliases of
TablishAliasesNone -> S.empty
TablishAliasesT (TableAlias _ name _) -> S.singleton name
TablishAliasesTC (TableAlias _ name _) _ -> S.singleton name
tablishToTableAlias (TablishJoin _ (JoinSemi _) _ _ _) =
error "this shouldn't happen: no SEMI JOIN in Presto"
tablishToTableAlias (TablishJoin _ _ _ lTablish rTablish) =
tablishToTableAlias lTablish `S.union` tablishToTableAlias rTablish
tableNameToTableAlias :: OQTableName Range -> Set Text
tableNameToTableAlias (QTableName _ Nothing tname) = S.singleton tname
tableNameToTableAlias (QTableName _ (Just (QSchemaName _ _ _ SessionSchema)) _) =
error "this shouldn't happen: no SessionSchema in Presto"
tableNameToTableAlias (QTableName _ (Just (QSchemaName _ Nothing sname _)) tname) =
S.fromList [ tname
, sname <> "." <> tname
]
tableNameToTableAlias (QTableName _ (Just (QSchemaName _ (Just (DatabaseName _ dname)) sname _)) tname) =
S.fromList [ tname
, sname <> "." <> tname
, dname <> "." <> sname <> "." <> tname
]
introduceAliases :: Set Text -> ParserScope -> ParserScope
introduceAliases aliases = \ scope ->
let unioned = case selectTableAliases scope of
Nothing -> aliases
Just existing -> S.union existing aliases
in scope { selectTableAliases = Just unioned }
fromP :: Parser (SelectFrom RawNames Range)
fromP = do
r <- Tok.fromP
relations <- relationP `sepBy1` Tok.commaP
let r' = foldl (<>) r $ fmap getInfo relations
return $ SelectFrom r' relations
relationP :: Parser (Tablish RawNames Range)
relationP = do
table <- sampledRelationP
joins <- fmap (appEndo . fold . reverse) $ many $ Endo <$> joinP
return $ joins table
sampledRelationP :: Parser (Tablish RawNames Range)
sampledRelationP = do
table <- aliasedRelationP
_ <- optional tableSampleP
return table
where
tableSampleP :: Parser Range
tableSampleP = do
s <- Tok.tableSampleP
_ <- sampleTypeP
_ <- Tok.openP
_ <- numberExprP
e <- Tok.closeP
return $ s <> e
sampleTypeP :: Parser Range
sampleTypeP = choice [ Tok.bernoulliP, Tok.systemP, Tok.poissonizedP ]
aliasedRelationP :: Parser (Tablish RawNames Range)
aliasedRelationP = do
let placeholder = error "placeholder aliases never got replaced!"
t <- choice $
[ do
name <- tableNameP
return $ TablishTable (getInfo name) placeholder name
, do
r1 <- Tok.unnestP
_ <- Tok.openP
let lateralViewOuter = Nothing
args <- exprP `sepBy1` Tok.commaP
r2 <- Tok.closeP
let lateralViewExprs = [FunctionExpr (r1 <> r2) (QFunctionName r1 Nothing "unnest") notDistinct args [] Nothing Nothing]
(lateralViewWithOrdinality, r3) <- option (False, r2) $ do
_ <- Tok.withP
(True, ) <$> Tok.ordinalityP
let lateralViewInfo = r1 <> r3
lateralViewAliases = placeholder
return $ TablishLateralView lateralViewInfo LateralView{..} Nothing
, P.between Tok.openP Tok.closeP $ choice
[ relationP
, do
q <- queryP
return $ TablishSubQuery (getInfo q) placeholder q
]
]
as <- tablishAliasesP
let withAliases = case t of
TablishTable info _ tableRef -> TablishTable info as tableRef
TablishSubQuery info _ query -> TablishSubQuery info as query
TablishJoin _ _ _ _ _ -> error "shouldn't happen"
TablishLateralView info LateralView{..} lhs -> TablishLateralView info LateralView{lateralViewAliases = as, ..} lhs
return withAliases
tablishAliasesP :: Parser (TablishAliases Range)
tablishAliasesP = do
option TablishAliasesNone $ try $ do
_ <- optional Tok.asP
tAlias@(TableAlias _ name _) <- tableAliasP
when (TL.toLower name == "tablesample") $ P.lookAhead $ P.notFollowedBy sampleTypeP
option (TablishAliasesT tAlias) $ do
cAliases <- P.between Tok.openP Tok.closeP $ columnAliasP `sepBy1` Tok.commaP
return $ TablishAliasesTC tAlias cAliases
tableAliasP :: Parser (TableAlias Range)
tableAliasP = do
(name, r) <- Tok.tableNameP
makeTableAlias r name
columnAliasP :: Parser (ColumnAlias Range)
columnAliasP = do
(name, r) <- Tok.columnNameP
makeColumnAlias r name
joinP :: Parser (Tablish RawNames Range -> Tablish RawNames Range)
joinP = crossJoinP <|> regularJoinP <|> naturalJoinP
where
crossJoinP :: Parser (Tablish RawNames Range -> Tablish RawNames Range)
crossJoinP = do
r <- Tok.crossP
r'<- Tok.joinP
rhs <- sampledRelationP
let info = r <> r'
joinType = JoinInner info
condition = JoinOn $ ConstantExpr info $ BooleanConstant info True
return $ \ lhs ->
TablishJoin (getInfo lhs <> getInfo rhs) joinType condition lhs rhs
regularJoinP :: Parser (Tablish RawNames Range -> Tablish RawNames Range)
regularJoinP = do
joinType <- joinTypeP
rhs <- relationP
condition <- choice
[ do
_ <- Tok.onP <?> "condition in join clause"
JoinOn <$> exprP
, do
s <- Tok.usingP <?> "using in join clause"
_ <- Tok.openP
names <- flip sepBy1 Tok.commaP $ do
(name, r) <- Tok.columnNameP
pure $ QColumnName r None name
e <- Tok.closeP
return $ JoinUsing (s <> e) names
]
return $ \ lhs ->
TablishJoin (getInfo rhs <> getInfo lhs) joinType condition lhs rhs
naturalJoinP :: Parser (Tablish RawNames Range -> Tablish RawNames Range)
naturalJoinP = do
r <- Tok.naturalP
joinType <- joinTypeP
rhs <- sampledRelationP
let condition = JoinNatural r Unused
return $ \ lhs ->
TablishJoin (getInfo rhs <> getInfo lhs) joinType condition lhs rhs
joinTypeP :: Parser (JoinType Range)
joinTypeP = do
maybeJoinType <- optionMaybe $ innerJoinTypeP <|> outerJoinTypeP
Tok.joinP >>= \ r -> return $ case maybeJoinType of
Nothing -> JoinInner r
Just joinType -> (<> r) <$> joinType
innerJoinTypeP :: Parser (JoinType Range)
innerJoinTypeP = JoinInner <$> Tok.innerP
outerJoinTypeP :: Parser (JoinType Range)
outerJoinTypeP = do
joinType <- choice
[ JoinLeft <$> Tok.leftP
, JoinRight <$> Tok.rightP
, JoinFull <$> Tok.fullP
]
optional Tok.outerP
return joinType
databaseNameP :: Parser (DatabaseName Range)
databaseNameP = do
(db, r) <- Tok.databaseNameP
return $ DatabaseName r db
unqualifiedSchemaNameP :: Parser (UQSchemaName Range)
unqualifiedSchemaNameP = uncurry mkNormalSchema <$> Tok.schemaNameP
schemaNameP :: Parser (SchemaName RawNames Range)
schemaNameP = choice
[ try qualifiedSchemaNameP
, do
uqsn <- unqualifiedSchemaNameP
pure uqsn { schemaNameDatabase = Nothing }
]
where
qualifiedSchemaNameP :: Parser (SchemaName RawNames Range)
qualifiedSchemaNameP = do
db <- databaseNameP
_ <- Tok.dotP
s <- unqualifiedSchemaNameP
return s { schemaNameDatabase = Just db }
unqualifiedTableNameP :: Parser (UQTableName Range)
unqualifiedTableNameP = do
(t, r) <- Tok.tableNameP
return $ QTableName r None t
tableNameP :: Parser (TableRef RawNames Range)
tableNameP = choice
[ try qualifiedTableNameP
, do
uqtn <- unqualifiedTableNameP
pure uqtn { tableNameSchema = Nothing }
]
where
qualifiedTableNameP :: Parser (TableRef RawNames Range)
qualifiedTableNameP = choice
[ try $ do
d <- databaseNameP
_ <- Tok.dotP
s <- unqualifiedSchemaNameP
_ <- Tok.dotP
t <- unqualifiedTableNameP
return t { tableNameSchema = Just s { schemaNameDatabase = Just d } }
, do
s <- unqualifiedSchemaNameP
_ <- Tok.dotP
t <- unqualifiedTableNameP
return $ t { tableNameSchema = Just s { schemaNameDatabase = Nothing } }
]
unqualifiedColumnNameP :: Parser (UQColumnName Range)
unqualifiedColumnNameP = do
(c, r) <- Tok.columnNameP
return $ QColumnName r None c
columnNameP :: Parser (ColumnRef RawNames Range)
columnNameP = choice
[ try qualifiedColumnNameP
, do
uqcn <- unqualifiedColumnNameP
pure uqcn { columnNameTable = Nothing }
]
where
qualifiedColumnNameP :: Parser (ColumnRef RawNames Range)
qualifiedColumnNameP = choice
[ try $ do
d@(DatabaseName _ dName) <- databaseNameP
_ <- Tok.dotP
s@(QSchemaName _ _ sName _) <- unqualifiedSchemaNameP
_ <- Tok.dotP
t@(QTableName _ _ tName) <- unqualifiedTableNameP
checkTableNameInScopeP $ dName <> "." <> sName <> "." <> tName
_ <- Tok.dotP
c <- unqualifiedColumnNameP
return c { columnNameTable = Just t { tableNameSchema = Just s { schemaNameDatabase = Just d } } }
, try $ do
s@(QSchemaName _ _ sName _) <- unqualifiedSchemaNameP
_ <- Tok.dotP
t@(QTableName _ _ tName) <- unqualifiedTableNameP
checkTableNameInScopeP $ sName <> "." <> tName
_ <- Tok.dotP
c <- unqualifiedColumnNameP
return c { columnNameTable = Just t { tableNameSchema = Just s { schemaNameDatabase = Nothing } } }
, do
t@(QTableName _ _ tName) <- unqualifiedTableNameP
checkTableNameInScopeP tName
_ <- Tok.dotP
c <- unqualifiedColumnNameP
return c { columnNameTable = Just t { tableNameSchema = Nothing } }
]
checkTableNameInScopeP :: Text -> Parser ()
checkTableNameInScopeP name = do
maybeScope <- asks selectTableAliases
case maybeScope of
Just scope ->
case L.find (==name) scope of
Just _ -> return ()
Nothing -> fail $ "Table " ++ (show name) ++
" doesn't exist in table scope " ++ show maybeScope
Nothing -> return ()
functionNameP :: Parser (FunctionName Range)
functionNameP = choice
[ try $ do
d <- databaseNameP
_ <- Tok.dotP
s <- unqualifiedSchemaNameP
_ <- Tok.dotP
(f, r) <- Tok.functionNameP
return $ QFunctionName (getInfo d <> r) (Just s { schemaNameDatabase = Just d }) f
, try $ do
s <- unqualifiedSchemaNameP
_ <- Tok.dotP
(f, r) <- Tok.functionNameP
return $ QFunctionName (getInfo s <> r) (Just s { schemaNameDatabase = Nothing }) f
, do
(f, r) <- Tok.functionNameP
return $ QFunctionName r Nothing f
]
selectStarP :: Parser (Selection RawNames Range)
selectStarP = choice
[ do
r <- Tok.starP
return $ SelectStar r Nothing Unused
, try $ do
t <- unqualifiedTableNameP
_ <- Tok.dotP
r' <- Tok.starP
return $ SelectStar (getInfo t <> r') (Just t { tableNameSchema = Nothing }) Unused
, try $ do
s <- unqualifiedSchemaNameP
_ <- Tok.dotP
t <- unqualifiedTableNameP
_ <- Tok.dotP
r'' <- Tok.starP
return $ SelectStar (getInfo s <> r'') (Just t { tableNameSchema = Just s { schemaNameDatabase = Nothing }}) Unused
, try $ do
d <- databaseNameP
_ <- Tok.dotP
s <- unqualifiedSchemaNameP
_ <- Tok.dotP
t <- unqualifiedTableNameP
_ <- Tok.dotP
r'' <- Tok.starP
return $ SelectStar (getInfo d <> r'') (Just t { tableNameSchema = Just s { schemaNameDatabase = Just d }}) Unused
]
selectionP :: Integer -> Parser (Selection RawNames Range)
selectionP idx = try selectStarP <|> do
expr <- exprP
alias <- aliasP expr idx
return $ SelectExpr (getInfo alias <> getInfo expr) [alias] expr
where
aliasP :: Expr RawNames Range -> Integer -> Parser (ColumnAlias Range)
aliasP expr idx' = choice
[ do
optional Tok.asP
(name, r) <- Tok.columnNameP
makeColumnAlias r name
, makeExprAlias expr idx'
]
countingSepBy1 :: (Integer -> Parser b) -> Parser c -> Parser [b]
countingSepBy1 f sep = do
x <- f 0
xs <- rest 1
pure (x:xs)
where
rest n = choice
[ do
_ <- sep
x <- f n
xs <- rest (n + 1)
pure (x:xs)
, pure []
]
makeTableAlias :: Range -> Text -> Parser (TableAlias Range)
makeTableAlias r alias = TableAlias r alias . TableAliasId <$> getNextCounter
makeColumnAlias :: Range -> Text -> Parser (ColumnAlias Range)
makeColumnAlias r alias = ColumnAlias r alias . ColumnAliasId <$> getNextCounter
makeDummyAlias :: Range -> Integer -> Parser (ColumnAlias Range)
makeDummyAlias r idx = makeColumnAlias r $ TL.pack $ "_col" ++ show idx
makeExprAlias :: Expr RawNames Range -> Integer -> Parser (ColumnAlias Range)
makeExprAlias (BinOpExpr info _ _ _) idx = makeDummyAlias info idx
makeExprAlias (UnOpExpr info _ _) idx = makeDummyAlias info idx
makeExprAlias (LikeExpr info _ _ _ _) idx = makeDummyAlias info idx
makeExprAlias (CaseExpr info _ _) idx = makeDummyAlias info idx
makeExprAlias (ColumnExpr info (QColumnName _ _ name)) _ = makeColumnAlias info name
makeExprAlias (ConstantExpr info _) idx = makeDummyAlias info idx
makeExprAlias (InListExpr info _ _) idx = makeDummyAlias info idx
makeExprAlias (InSubqueryExpr info _ _) idx = makeDummyAlias info idx
makeExprAlias (BetweenExpr info _ _ _) idx = makeDummyAlias info idx
makeExprAlias (OverlapsExpr _ _ _) _ = fail "Unsupported overlaps expr in Presto: unused expr-type in this dialect"
makeExprAlias (AtTimeZoneExpr info _ _) idx = makeDummyAlias info idx
makeExprAlias (FunctionExpr info _ _ _ _ _ _) idx = makeDummyAlias info idx
makeExprAlias (SubqueryExpr info _) idx = makeDummyAlias info idx
makeExprAlias (ArrayExpr info _) idx = makeDummyAlias info idx
makeExprAlias (ExistsExpr info _) idx = makeDummyAlias info idx
makeExprAlias (FieldAccessExpr info _ _) idx = makeDummyAlias info idx
makeExprAlias (ArrayAccessExpr info _ _) idx = makeDummyAlias info idx
makeExprAlias (TypeCastExpr _ _ expr _) idx = makeExprAlias expr idx
makeExprAlias (VariableSubstitutionExpr _) _ = fail "Unsupported variable substitution in Presto: unused expr-type in this dialect"
unOpP :: Text -> Parser (Expr RawNames Range -> Expr RawNames Range)
unOpP op = do
r <- Tok.symbolP op
return $ \ expr -> UnOpExpr (r <> getInfo expr) (Operator op) expr
binOpP :: Text -> Parser (Expr RawNames Range -> Expr RawNames Range -> Expr RawNames Range)
binOpP op = do
r <- Tok.symbolP op
let r' lhs rhs = sconcat $ r :| map getInfo [lhs, rhs]
return $ \ lhs rhs -> BinOpExpr (r' lhs rhs) (Operator op) lhs rhs
exprP :: Parser (Expr RawNames Range)
exprP = orExprP
orExprP :: Parser (Expr RawNames Range)
orExprP = andExprP `chainl1` (Tok.orP >>= \ r -> return (BinOpExpr r "OR"))
andExprP :: Parser (Expr RawNames Range)
andExprP = notExprP `chainl1`
(Tok.andP >>= \ r -> return $ BinOpExpr r "AND")
notP :: Parser (Expr RawNames Range -> Expr RawNames Range)
notP = (\ r -> UnOpExpr r "NOT") <$> Tok.notP
notExprP :: Parser (Expr RawNames Range)
notExprP = do
nots <- appEndo . fold . reverse . map Endo <$> many notP
expr <- predicatedExprP
return $ nots expr
predicatedExprP :: Parser (Expr RawNames Range)
predicatedExprP = do
value <- valueExprP
predicate <- option id predicateP
return $ predicate value
valueExprP :: Parser (Expr RawNames Range)
valueExprP = concatExprP
concatExprP :: Parser (Expr RawNames Range)
concatExprP = sumExprP `chainl1` binOpP "||"
sumExprP :: Parser (Expr RawNames Range)
sumExprP = productExprP `chainl1` opP
where
opP = choice $ map binOpP ["+", "-"]
productExprP :: Parser (Expr RawNames Range)
productExprP = negateExprP `chainl1` opP
where
opP = choice $ map binOpP ["*", "/", "%"]
negateExprP :: Parser (Expr RawNames Range)
negateExprP = do
neg <- option id $ choice $ map unOpP ["+", "-"]
expr <- atTimeZoneExprP
return $ neg expr
intervalP :: Parser (Expr RawNames Range)
intervalP = do
r <- Tok.intervalP
sign <- option [] $ pure <$> signP
str <- stringConstantP
from <- intervalFieldP
maybeTo <- optionMaybe $ Tok.toP >> intervalFieldP
let (info, attrs) =
case maybeTo of
Nothing -> (r <> getInfo from, sign ++ [from])
Just to -> (r <> getInfo to, sign ++ [from, to])
return $ TypeCastExpr info CastFailureError str $ PrimitiveDataType info "interval" attrs
where
signP :: Parser (DataTypeParam Range)
signP = do
(sign, r) <- choice [ ("+",) <$> Tok.symbolP "+"
, ("-",) <$> Tok.symbolP "-"
]
pure $ DataTypeParamConstant $ StringConstant r sign
intervalFieldP :: Parser (DataTypeParam Range)
intervalFieldP = do
(field, r) <- Tok.intervalFieldP
pure $ DataTypeParamConstant $ StringConstant r $ TL.encodeUtf8 field
atTimeZoneExprP :: Parser (Expr RawNames Range)
atTimeZoneExprP = foldl (flip ($)) <$> primaryExprP <*> many atTimeZoneP
where
atTimeZoneP :: Parser (Expr RawNames Range -> Expr RawNames Range)
atTimeZoneP = do
_ <- Tok.atP
_ <- Tok.timezoneP
tz <- choice [stringConstantP, intervalP]
return $ \ expr ->
AtTimeZoneExpr (getInfo expr <> getInfo tz) expr tz
stringConstantP :: Parser (Expr RawNames Range)
stringConstantP = do
(str, r) <- Tok.stringP
return $ ConstantExpr r (StringConstant r str)
primaryExprP :: Parser (Expr RawNames Range)
primaryExprP = foldl (flip ($)) <$> baseP <*> many (arrayAccessP <|> structAccessP)
where
baseP = choice
[ extractPrimaryExprP
, normalizePrimaryExprP
, try substringPrimaryExprP
, try positionPrimaryExprP
, bareFuncPrimaryExprP
, arrayPrimaryExprP
, castPrimaryExprP
, casePrimaryExprP
, existsPrimaryExprP
, try subqueryPrimaryExprP
, implicitRowPrimaryExprP
, try rowPrimaryExprP
, try functionCallPrimaryExprP
, parameterPrimaryExprP
, binaryLiteralPrimaryExprP
, intervalP
, try $ constantPrimaryExprP
, columnRefPrimaryExprP
]
extractPrimaryExprP :: Parser (Expr RawNames Range)
extractPrimaryExprP = do
r <- Tok.extractP
_ <- Tok.openP
unit <- unitP
_ <- Tok.fromP
expr <- valueExprP
r' <- Tok.closeP
return $ FunctionExpr (r <> r') (QFunctionName r Nothing "extract") notDistinct [unit, expr] [] Nothing Nothing
where
unitP = do
(unit, r) <- Tok.extractUnitP
return $ ConstantExpr r $ StringConstant r $ TL.encodeUtf8 unit
normalizePrimaryExprP :: Parser (Expr RawNames Range)
normalizePrimaryExprP = do
r <- Tok.normalizeP
_ <- Tok.openP
strExpr <- valueExprP
(form, formR) <- option ("nfc", getInfo strExpr) $ Tok.commaP >> Tok.normalFormP
r' <- Tok.closeP
let formExpr = ConstantExpr formR $ StringConstant formR $ TL.encodeUtf8 form
return $ FunctionExpr (r <> r') (QFunctionName r Nothing "normalize") notDistinct [strExpr, formExpr] [] Nothing Nothing
substringPrimaryExprP :: Parser (Expr RawNames Range)
substringPrimaryExprP = do
r <- Tok.substringP
_ <- Tok.openP
str <- valueExprP
_ <- Tok.fromP
start <- valueExprP
maybeLen <- optionMaybe $ Tok.forP >> valueExprP
r' <- Tok.closeP
let args = catMaybes [Just str, Just start, maybeLen]
return $ FunctionExpr (r <> r') (QFunctionName r Nothing "substring") notDistinct args [] Nothing Nothing
bareFuncPrimaryExprP :: Parser (Expr RawNames Range)
bareFuncPrimaryExprP = do
(func, r, alwaysBare) <- Tok.possiblyBareFuncP
let name = QFunctionName r Nothing func
(args, r') <- if alwaysBare
then pure ([], r)
else option ([], r) precisionP
return $ FunctionExpr (r <> r') name notDistinct args [] Nothing Nothing
where
precisionP = do
s <- Tok.openP
numExpr <- numberExprP
e <- Tok.closeP
return ([numExpr], s <> e)
arrayPrimaryExprP :: Parser (Expr RawNames Range)
arrayPrimaryExprP = do
r <- Tok.arrayP
_ <- Tok.openBracketP
exprs <- exprP `sepBy1` Tok.commaP
r' <- Tok.closeBracketP
return $ ArrayExpr (r <> r') exprs
dataTypeP :: Parser (DataType Range)
dataTypeP = foldl (flip ($)) <$> typeP <*> many arraySuffixP
where
typeP :: Parser (DataType Range)
typeP = choice [ arrayTypeP
, mapTypeP
, rowTypeP
, baseTypeP
]
arraySuffixP :: Parser (DataType Range -> DataType Range)
arraySuffixP = ArrayDataType <$> Tok.arrayP
baseTypeP = do
(name, r) <- Tok.typeNameP
let typeParameterP = choice
[ DataTypeParamConstant <$> numberConstantP
, DataTypeParamType <$> dataTypeP
]
args <- option [] $ P.between Tok.openP Tok.closeP $ typeParameterP `sepBy1` Tok.commaP
return $ PrimitiveDataType r name args
arrayTypeP = do
s <- Tok.arrayP
_ <- Tok.openAngleP
itemType <- dataTypeP
e <- Tok.closeAngleP
return $ ArrayDataType (s <> e) itemType
mapTypeP = do
s <- Tok.mapP
_ <- Tok.openAngleP
keyType <- dataTypeP
_ <- Tok.commaP
valueType <- dataTypeP
e <- Tok.closeAngleP
return $ MapDataType (s <> e) keyType valueType
rowTypeP = do
s <- Tok.rowP
_ <- Tok.openP
let fieldP = do
(name, _) <- Tok.structFieldNameP
type_ <- dataTypeP
return (name, type_)
fields <- fieldP `sepBy1` Tok.commaP
e <- Tok.closeP
return $ StructDataType (s <> e) fields
castPrimaryExprP :: Parser (Expr RawNames Range)
castPrimaryExprP = do
(onFail, r) <- Tok.castFuncP
_ <- Tok.openP
e <- exprP
_ <- Tok.asP
t <- dataTypeP
r' <- Tok.closeP
return $ TypeCastExpr (r <> r') onFail e t
casePrimaryExprP :: Parser (Expr RawNames Range)
casePrimaryExprP = do
r <- Tok.caseP
whens <- choice
[ P.many1 $ do
_ <- Tok.whenP
condition <- exprP
_ <- Tok.thenP
result <- exprP
return (condition, result)
, do
value <- valueExprP
P.many1 $ do
whenr <- Tok.whenP
condition <- BinOpExpr whenr "=" value <$> exprP
_ <- Tok.thenP
result <- exprP
return (condition, result)
]
melse <- optionMaybe $ do
_ <- Tok.elseP
exprP
r' <- Tok.endP
return $ CaseExpr (r <> r') whens melse
existsPrimaryExprP :: Parser (Expr RawNames Range)
existsPrimaryExprP = do
r <- Tok.existsP
_ <- Tok.openP
query <- queryP
r' <- Tok.closeP
return $ ExistsExpr (r <> r') query
subqueryPrimaryExprP :: Parser (Expr RawNames Range)
subqueryPrimaryExprP = P.between Tok.openP Tok.closeP $ do
query <- queryP
return $ SubqueryExpr (getInfo query) query
functionCallPrimaryExprP :: Parser (Expr RawNames Range)
functionCallPrimaryExprP = do
name@(QFunctionName r _ _) <- functionNameP
(distinct, args) <- P.between Tok.openP Tok.closeP $ choice
[ do
r' <- Tok.starP
return (notDistinct, [ConstantExpr r' $ NumericConstant r' "1"])
, do
isDistinct <- option notDistinct distinctP
exprs <- exprP `sepBy` Tok.commaP
return (isDistinct, exprs)
]
let params = []
filter' <- optionMaybe filterP
over <- optionMaybe overP
let info :: Range
info = sconcat $ r :| concat [ maybe r getInfo filter' : []
, maybe r getInfo over : []
, map getInfo args
]
return $ FunctionExpr info name distinct args params filter' over
where
filterP = do
r <- Tok.filterP
_ <- Tok.openP
_ <- Tok.whereP
expr <- exprP
r' <- Tok.closeP
return $ Filter (r <> r') expr
overP :: Parser (OverSubExpr RawNames Range)
overP = do
start <- Tok.overP
_ <- Tok.openP
partition <- optionMaybe partitionP
order <- option [] orderInWindowClauseP
frame <- optionMaybe frameP
end <- Tok.closeP
let info = start <> end
return $ OverWindowExpr info $ WindowExpr info partition order frame
where
partitionP :: Parser (Partition RawNames Range)
partitionP = do
r <- Tok.partitionP
_ <- Tok.byP
exprs <- exprP `sepBy1` Tok.commaP
return $ PartitionBy (sconcat $ r :| map getInfo exprs) exprs
frameP :: Parser (Frame Range)
frameP = do
ftype <- choice
[ RowFrame <$> Tok.rowsP
, RangeFrame <$> Tok.rangeP
]
choice
[ do
_ <- Tok.betweenP
start <- frameBoundP
_ <- Tok.andP
end <- frameBoundP
let r = getInfo ftype <> getInfo end
return $ Frame r ftype start (Just end)
, do
start <- frameBoundP
let r = getInfo ftype <> getInfo start
return $ Frame r ftype start Nothing
]
frameBoundP :: Parser (FrameBound Range)
frameBoundP = choice
[ fmap Unbounded $ (<>)
<$> Tok.unboundedP
<*> choice [ Tok.precedingP, Tok.followingP ]
, fmap CurrentRow $ (<>) <$> Tok.currentP <*> Tok.rowP
, numberConstantP >>= \ expr -> choice
[ Tok.precedingP >>= \ r ->
return $ Preceding (getInfo expr <> r) expr
, Tok.followingP >>= \ r ->
return $ Following (getInfo expr <> r) expr
]
]
orderTopLevelP :: Parser (Range, [Order RawNames Range])
orderTopLevelP = orderExprP False True
orderInWindowClauseP :: Parser [Order RawNames Range]
orderInWindowClauseP = snd <$> orderExprP True False
orderExprP :: Bool -> Bool -> Parser (Range, [Order RawNames Range])
orderExprP nullsClausePermitted positionalReferencesPermitted = do
r <- Tok.orderP
_ <- Tok.byP
orders <- helperP `sepBy1` Tok.commaP
let r' = getInfo $ last orders
return (r <> r', orders)
where
helperP :: Parser (Order RawNames Range)
helperP = do
expr <- exprP
let posOrExpr = if positionalReferencesPermitted
then handlePositionalReferences expr
else PositionOrExprExpr expr
dir <- directionP
nulls <- case (nullsClausePermitted, dir) of
(False, _) -> return $ NullsAuto Nothing
(True, OrderAsc _) -> option (NullsLast Nothing) nullsP
(True, OrderDesc _) -> option (NullsFirst Nothing) nullsP
let info = (getInfo expr) ?<> (getInfo dir) <> (getInfo nulls)
return $ Order info posOrExpr dir nulls
directionP :: Parser (OrderDirection (Maybe Range))
directionP = option (OrderAsc Nothing) $ choice
[ OrderAsc . Just <$> Tok.ascP
, OrderDesc . Just <$> Tok.descP
]
nullsP :: Parser (NullPosition (Maybe Range))
nullsP = do
r <- Tok.nullsP
choice
[ Tok.firstP >>= \ r' -> return $ NullsFirst $ Just $ r <> r'
, Tok.lastP >>= \ r' -> return $ NullsLast $ Just $ r <> r'
]
rowPrimaryExprP :: Parser (Expr RawNames Range)
rowPrimaryExprP = do
r <- Tok.rowP
_ <- Tok.openP
exprs <- exprP `sepBy1` Tok.commaP
r' <- Tok.closeP
let name = QFunctionName r Nothing "row"
return $ FunctionExpr (r <> r') name notDistinct exprs [] Nothing Nothing
implicitRowPrimaryExprP :: Parser (Expr RawNames Range)
implicitRowPrimaryExprP = do
r <- Tok.openP
exprs <- exprP `sepBy1` Tok.commaP
r' <- Tok.closeP
case exprs of
[] -> error "this shouldn't happen with sepBy1"
[e] -> return e
es -> let name = QFunctionName r Nothing "implicit row"
in return $ FunctionExpr (r <> r') name notDistinct es [] Nothing Nothing
positionPrimaryExprP :: Parser (Expr RawNames Range)
positionPrimaryExprP = do
r <- Tok.positionP
_ <- Tok.openP
substring <- valueExprP
_ <- Tok.inP
string <- valueExprP
r' <- Tok.closeP
return $ FunctionExpr (r <> r') (QFunctionName r Nothing "position") notDistinct [substring, string] [] Nothing Nothing
parameterPrimaryExprP :: Parser (Expr RawNames Range)
parameterPrimaryExprP = do
r <- Tok.questionMarkP
return $ ConstantExpr r $ ParameterConstant r
binaryLiteralPrimaryExprP :: Parser (Expr RawNames Range)
binaryLiteralPrimaryExprP = do
(bytes, r) <- Tok.binaryLiteralP
return $ ConstantExpr r $ StringConstant r bytes
constantPrimaryExprP :: Parser (Expr RawNames Range)
constantPrimaryExprP = do
val <- choice [ stringP
, booleanP
, numberConstantP
, typedConstantP
, nullP
]
return $ ConstantExpr (getInfo val) val
where
stringP = uncurry (flip StringConstant) <$> Tok.stringP
booleanP = uncurry (flip BooleanConstant) <$> choice
[ Tok.trueP >>= \ r -> return (True, r)
, Tok.falseP >>= \ r -> return (False, r)
]
typedConstantP = do
(typeStr, r) <- Tok.typedConstantTypeP
let dataType = PrimitiveDataType r typeStr []
(string, r') <- first TL.decodeUtf8 <$> Tok.stringP
pure $ TypedConstant (r <> r') string dataType
nullP = NullConstant <$> Tok.nullP
numberExprP :: Parser (Expr RawNames Range)
numberExprP = do
num <- numberConstantP
return $ ConstantExpr (getInfo num) num
numberConstantP :: Parser (Constant Range)
numberConstantP = uncurry (flip NumericConstant) <$> Tok.numberP
columnRefPrimaryExprP :: Parser (Expr RawNames Range)
columnRefPrimaryExprP = do
name <- columnNameP
return $ ColumnExpr (getInfo name) name
structAccessP :: Parser (Expr RawNames Range -> Expr RawNames Range)
structAccessP = do
_ <- Tok.dotP
field <- structFieldNameP
return $ \ struct ->
let r = getInfo struct <> getInfo field
in FieldAccessExpr r struct field
where
structFieldNameP :: Parser (StructFieldName Range)
structFieldNameP = uncurry (flip StructFieldName) <$> Tok.structFieldNameP
arrayAccessP :: Parser (Expr RawNames Range -> Expr RawNames Range)
arrayAccessP = do
_ <- Tok.openBracketP
index <- valueExprP
e <- Tok.closeBracketP
return $ \ expr ->
let exprR = getInfo expr <> e
in ArrayAccessExpr exprR expr index
predicateP :: Parser (Expr RawNames Range -> Expr RawNames Range)
predicateP = choice
[ isPredicateP
, try likePredicateP
, try inPredicateP
, betweenPredicateP
, try quantifiedComparisonPredicateP
, unquantifiedComparisonPredicateP
]
optionalNotWrapper :: Parser (Expr RawNames Range -> Expr RawNames Range)
optionalNotWrapper = do
maybeNot <- optionMaybe Tok.notP
return $ maybe id (\ r -> UnOpExpr r "NOT") maybeNot
isPredicateP :: Parser (Expr RawNames Range -> Expr RawNames Range)
isPredicateP = do
r <- Tok.isP
notWrapper <- optionalNotWrapper
predicate <- choice [ Left <$> (Tok.distinctP >> Tok.fromP >> valueExprP)
, Right <$> Tok.nullP
]
return $ case predicate of
Left expr ->
\ expr' -> notWrapper $ BinOpExpr (r <> getInfo expr) "IS DISTINCT FROM" expr expr'
Right nullKeyword ->
\ expr' -> notWrapper $ UnOpExpr (r <> nullKeyword) "ISNULL" expr'
likePredicateP :: Parser (Expr RawNames Range -> Expr RawNames Range)
likePredicateP = do
notWrapper <- optionalNotWrapper
r <- Tok.likeP
pattern <- Pattern <$> valueExprP
escape <- optionMaybe $ do
_ <- Tok.escapeP
Escape <$> valueExprP
return $ \ expr -> notWrapper $ LikeExpr r "LIKE" escape pattern expr
inPredicateP :: Parser (Expr RawNames Range -> Expr RawNames Range)
inPredicateP = do
notWrapper <- optionalNotWrapper
r <- Tok.inP
_ <- Tok.openP
predicate <- choice
[ Left <$> queryP
, Right <$> exprP `sepBy1` Tok.commaP
]
r' <- Tok.closeP
return $ case predicate of
Left query ->
\ expr -> notWrapper $ InSubqueryExpr (r <> r') query expr
Right exprs ->
\ expr -> notWrapper $ InListExpr (r <> r') exprs expr
betweenPredicateP :: Parser (Expr RawNames Range -> Expr RawNames Range)
betweenPredicateP = do
notWrapper <- optionalNotWrapper
_ <- Tok.betweenP
lower <- valueExprP
_ <- Tok.andP
upper <- valueExprP
return $ \ expr -> notWrapper $ BetweenExpr (getInfo expr <> getInfo upper) lower upper expr
quantifiedComparisonPredicateP :: Parser (Expr RawNames Range -> Expr RawNames Range)
quantifiedComparisonPredicateP = do
(op, _) <- Tok.comparisonOperatorP
(quantifier, _) <- Tok.comparisonQuantifierP
_ <- Tok.openP
query <- queryP
r <- Tok.closeP
let op' = Operator $ TL.unwords [op, quantifier]
subquery = SubqueryExpr (getInfo query) query
return $ \ expr -> BinOpExpr (getInfo expr <> r) op' expr subquery
unquantifiedComparisonPredicateP :: Parser (Expr RawNames Range -> Expr RawNames Range)
unquantifiedComparisonPredicateP = do
(op, _) <- Tok.comparisonOperatorP
rhs <- valueExprP
return $ \ lhs -> BinOpExpr (getInfo lhs <> getInfo rhs) (Operator op) lhs rhs
whereP :: Parser (SelectWhere RawNames Range)
whereP = do
r <- Tok.whereP
condition <- exprP
return $ SelectWhere (r <> getInfo condition) condition
handlePositionalReferences :: Expr RawNames Range -> PositionOrExpr RawNames Range
handlePositionalReferences e = case e of
ConstantExpr _ (NumericConstant _ n) | TL.all isDigit n -> PositionOrExprPosition (getInfo e) (read $ TL.unpack n) Unused
_ -> PositionOrExprExpr e
groupP :: Parser (SelectGroup RawNames Range)
groupP = do
r <- Tok.groupP
_ <- Tok.byP
optional distinctP
selectGroupGroupingElements <- concat <$> groupingElementP `sepBy1` Tok.commaP
let selectGroupInfo = foldl (<>) r $ fmap getInfo selectGroupGroupingElements
return SelectGroup{..}
where
toGroupingElement :: PositionOrExpr RawNames Range -> GroupingElement RawNames Range
toGroupingElement posOrExpr = GroupingElementExpr (getInfo posOrExpr) posOrExpr
groupingElementP :: Parser [GroupingElement RawNames Range]
groupingElementP = choice
[ singleExprP
, parenExprsP
, rollupP
, cubeP
, groupingSetsP
]
singleExprP = do
e <- exprP
return [toGroupingElement $ handlePositionalReferences e]
parenExprsP = do
_ <- Tok.openP
es <- exprP `sepBy` Tok.commaP
_ <- Tok.closeP
return $ map (toGroupingElement . handlePositionalReferences) es
toGroupingSet :: Range -> [Expr RawNames Range] -> GroupingElement RawNames Range
toGroupingSet r [] = GroupingElementSet r []
toGroupingSet _ exprs =
let s = getInfo $ head exprs
e = getInfo $ last exprs
in GroupingElementSet (s <> e) exprs
rollupP = do
_ <- Tok.rollupP
_ <- Tok.openP
cols <- columnRefPrimaryExprP `sepBy1` Tok.commaP
_ <- Tok.closeP
let dimensions = L.reverse $ L.inits cols
defaultRange = (getInfo $ head cols) <> (getInfo $ last cols)
return $ map (toGroupingSet defaultRange) dimensions
cubeP = do
_ <- Tok.cubeP
_ <- Tok.openP
cols <- columnRefPrimaryExprP `sepBy1` Tok.commaP
_ <- Tok.closeP
let dimensions = L.subsequences cols
defaultRange = (getInfo $ head cols) <> (getInfo $ last cols)
return $ map (toGroupingSet defaultRange) dimensions
groupingSetP = choice $
[ do
s <- Tok.openP
sets <- columnRefPrimaryExprP `sepBy1` Tok.commaP
e <- Tok.closeP
return $ GroupingElementSet (s <> e) sets
, do
col <- columnRefPrimaryExprP
return $ GroupingElementSet (getInfo col) [col]
]
groupingSetsP = do
_ <- Tok.groupingP
_ <- Tok.setsP
_ <- Tok.openP
sets <- groupingSetP `sepBy1` Tok.commaP
_ <- Tok.closeP
return sets
havingP :: Parser (SelectHaving RawNames Range)
havingP = do
r <- Tok.havingP
condition <- exprP
return $ SelectHaving (r <> getInfo condition) [condition]
deleteP :: Parser (Delete RawNames Range)
deleteP = do
r <- Tok.deleteP
_ <- Tok.fromP
table <- tableNameP
maybeExpr <- optionMaybe $ do
_ <- Tok.whereP
local (introduceAliases $ tableNameToTableAlias table) exprP
let r' = case maybeExpr of
Nothing -> getInfo table
Just expr -> getInfo expr
info = r <> r'
pure $ Delete info table maybeExpr
dropViewPrefixP :: Parser Range
dropViewPrefixP = do
s <- Tok.dropP
e <- Tok.viewP
pure $ s <> e
dropViewP :: Parser (DropView RawNames Range)
dropViewP = do
s <- dropViewPrefixP
dropViewIfExists <- optionMaybe ifExistsP
dropViewName <- tableNameP
let dropViewInfo = s <> getInfo dropViewName
pure DropView{..}
dropTableP :: Parser (DropTable RawNames Range)
dropTableP = do
s <- Tok.dropP
_ <- Tok.tableP
dropTableIfExists <- optionMaybe ifExistsP
table <- tableNameP
let dropTableInfo = s <> getInfo table
dropTableNames = table :| []
pure DropTable{..}
ifExistsP :: Parser Range
ifExistsP = do
s <- Tok.ifP
e <- Tok.existsP
pure $ s <> e
grantP :: Parser (Grant Range)
grantP = do
s <- Tok.grantP
e <- P.many1 Tok.notSemicolonP
return $ Grant (s <> (last e))
revokeP :: Parser (Revoke Range)
revokeP = do
s <- Tok.revokeP
e <- P.many1 Tok.notSemicolonP
return $ Revoke (s <> (last e))
insertP :: Parser (Insert RawNames Range)
insertP = do
r <- Tok.insertP
insertBehavior <- InsertAppend <$> Tok.intoP
insertTable <- tableNameP
insertColumns <- optionMaybe $ try $ do
_ <- Tok.openP
let oqColumnNameP = (\ (c, r') -> QColumnName r' Nothing c) <$> Tok.columnNameP
c:cs <- oqColumnNameP `sepBy1` Tok.commaP
_ <- Tok.closeP
pure (c :| cs)
insertValues <- choice
[ do
s <- Tok.valuesP
(e, rows) <- rowsOfValuesP
pure $ InsertExprValues (s <> e) rows
, InsertSelectValues <$> queryP
]
let insertInfo = r <> getInfo insertValues
pure Insert{..}
where
valueP :: Parser (DefaultExpr RawNames Range)
valueP = ExprValue <$> exprP
rowOfValuesP = do
s <- Tok.openP
x:xs <- valueP `sepBy1` Tok.commaP
e <- Tok.closeP
pure $ (s <> e, x :| xs)
rowsOfValuesP = do
rows <- rowOfValuesP `sepBy1` Tok.commaP
let infos = map fst rows
r:rs = map snd rows
pure $ (head infos <> last infos, r :| rs)
explainP :: Parser Range
explainP = do
s <- Tok.explainP
e <- getInfo <$> queryP
return $ s <> e
showP :: Parser Range
showP = do
s <- Tok.showP
e <- P.many1 Tok.notSemicolonP
return $ s <> (last e)
callP :: Parser Range
callP = do
s <- Tok.callP
e <- P.many1 Tok.notSemicolonP
return $ s <> (last e)
describeP :: Parser Range
describeP = do
s <- Tok.describeP
e <- P.many1 Tok.notSemicolonP
return $ s <> last e