module PostgresqlSyntax.Parsing where
import PostgresqlSyntax.Prelude hiding (expr, try, option, some, many, sortBy, filter, head, tail, bit, fromList)
import HeadedMegaparsec hiding (string)
import Control.Applicative.Combinators hiding (some)
import Control.Applicative.Combinators.NonEmpty
import PostgresqlSyntax.Extras.HeadedMegaparsec hiding (run)
import PostgresqlSyntax.Ast
import Text.Megaparsec (Stream, Parsec)
import qualified PostgresqlSyntax.Extras.HeadedMegaparsec as Extras
import qualified PostgresqlSyntax.Extras.NonEmpty as NonEmpty
import qualified Text.Megaparsec as Megaparsec
import qualified Text.Megaparsec.Char as MegaparsecChar
import qualified Text.Megaparsec.Char.Lexer as MegaparsecLexer
import qualified PostgresqlSyntax.KeywordSet as KeywordSet
import qualified PostgresqlSyntax.Predicate as Predicate
import qualified PostgresqlSyntax.Validation as Validation
import qualified Data.Text as Text
import qualified Data.List.NonEmpty as NonEmpty
import qualified Text.Builder as TextBuilder
import qualified Data.HashSet as HashSet
type Parser = HeadedParsec Void Text
run :: Parser a -> Text -> Either String a
run = Extras.run
commaSeparator :: Parser ()
commaSeparator = space *> char ',' *> endHead *> space
dotSeparator :: Parser ()
dotSeparator = space *> char '.' *> endHead *> space
inBrackets :: Parser a -> Parser a
inBrackets p = char '[' *> space *> p <* endHead <* space <* char ']'
inBracketsCont :: Parser a -> Parser (Parser a)
inBracketsCont p = char '[' *> endHead *> pure (space *> p <* endHead <* space <* char ']')
inParens :: Parser a -> Parser a
inParens p = char '(' *> space *> p <* endHead <* space <* char ')'
inParensCont :: Parser a -> Parser (Parser a)
inParensCont p = char '(' *> endHead *> pure (space *> p <* endHead <* space <* char ')')
inParensWithLabel :: (label -> content -> result) -> Parser label -> Parser content -> Parser result
inParensWithLabel _result _labelParser _contentParser = do
_label <- wrapToHead _labelParser
space
char '('
endHead
space
_content <- _contentParser
space
char ')'
pure (_result _label _content)
inParensWithClause :: Parser clause -> Parser content -> Parser content
inParensWithClause = inParensWithLabel (const id)
trueIfPresent :: Parser a -> Parser Bool
trueIfPresent p = option False (True <$ p)
quotedString :: Char -> Parser Text
quotedString q = do
char q
endHead
_tail <- parse $ let
collectChunks !bdr = do
chunk <- Megaparsec.takeWhileP Nothing (/= q)
let bdr' = bdr <> TextBuilder.text chunk
Megaparsec.try (consumeEscapedQuote bdr') <|> finish bdr'
consumeEscapedQuote bdr = do
MegaparsecChar.char q
MegaparsecChar.char q
collectChunks (bdr <> TextBuilder.char q)
finish bdr = do
MegaparsecChar.char q
return (TextBuilder.run bdr)
in collectChunks mempty
return _tail
quasiQuote :: Parser a -> Parser a
quasiQuote p = space *> p <* endHead <* space <* eof
preparableStmt =
SelectPreparableStmt <$> selectStmt <|>
InsertPreparableStmt <$> insertStmt <|>
UpdatePreparableStmt <$> updateStmt <|>
DeletePreparableStmt <$> deleteStmt
insertStmt = do
a <- optional (wrapToHead withClause <* space1)
keyword "insert"
space1
endHead
keyword "into"
space1
b <- insertTarget
space1
c <- insertRest
d <- optional (space1 *> onConflict)
e <- optional (space1 *> returningClause)
return (InsertStmt a b c d e)
insertTarget = do
a <- qualifiedName
endHead
b <- optional (space1 *> keyword "as" *> space1 *> endHead *> colId)
return (InsertTarget a b)
insertRest = asum [
DefaultValuesInsertRest <$ (keyword "default" *> space1 *> endHead *> keyword "values")
,
do
a <- optional (inParens insertColumnList <* space1)
b <- optional $ do
keyword "overriding"
space1
endHead
b <- overrideKind
space1
keyword "value"
space1
return b
c <- selectStmt
return (SelectInsertRest a b c)
]
overrideKind = asum [
UserOverrideKind <$ keyword "user",
SystemOverrideKind <$ keyword "system"
]
insertColumnList = sep1 commaSeparator insertColumnItem
insertColumnItem = do
a <- colId
endHead
b <- optional (space1 *> indirection)
return (InsertColumnItem a b)
onConflict = do
keyword "on"
space1
keyword "conflict"
space1
endHead
a <- optional (confExpr <* space1)
keyword "do"
space1
b <- onConflictDo
return (OnConflict a b)
confExpr = asum [
WhereConfExpr <$> inParens indexParams <*> optional (space *> whereClause)
,
ConstraintConfExpr <$> (keyword "on" *> space1 *> keyword "constraint" *> space1 *> endHead *> name)
]
onConflictDo = asum [
NothingOnConflictDo <$ keyword "nothing"
,
do
keyword "update"
space1
endHead
keyword "set"
space1
a <- setClauseList
b <- optional (space1 *> whereClause)
return (UpdateOnConflictDo a b)
]
returningClause = do
keyword "returning"
space1
endHead
targetList
updateStmt = do
a <- optional (wrapToHead withClause <* space1)
keyword "update"
space1
endHead
b <- relationExprOptAlias ["set"]
space1
keyword "set"
space1
c <- setClauseList
d <- optional (space1 *> fromClause)
e <- optional (space1 *> whereOrCurrentClause)
f <- optional (space1 *> returningClause)
return (UpdateStmt a b c d e f)
setClauseList = sep1 commaSeparator setClause
setClause = asum [
do
a <- inParens setTargetList
space
char '='
space
b <- aExpr
return (TargetListSetClause a b)
,
do
a <- setTarget
space
char '='
space
b <- aExpr
return (TargetSetClause a b)
]
setTarget = do
a <- colId
endHead
b <- optional (space1 *> indirection)
return (SetTarget a b)
setTargetList = sep1 commaSeparator setTarget
deleteStmt = do
a <- optional (wrapToHead withClause <* space1)
keyword "delete"
space1
endHead
keyword "from"
space1
b <- relationExprOptAlias ["using", "where", "returning"]
c <- optional (space1 *> usingClause)
d <- optional (space1 *> whereOrCurrentClause)
e <- optional (space1 *> returningClause)
return (DeleteStmt a b c d e)
usingClause = do
keyword "using"
space1
fromList
selectStmt = Left <$> selectNoParens <|> Right <$> selectWithParens
selectWithParens = inParens (WithParensSelectWithParens <$> selectWithParens <|> NoParensSelectWithParens <$> selectNoParens)
selectNoParens = withSelectNoParens <|> simpleSelectNoParens
sharedSelectNoParens _with = do
_select <- selectClause
_sort <- optional (space1 *> sortClause)
_limit <- optional (space1 *> selectLimit)
_forLocking <- optional (space1 *> forLockingClause)
return (SelectNoParens _with _select _sort _limit _forLocking)
simpleSelectNoParens = sharedSelectNoParens Nothing
withSelectNoParens = do
_with <- wrapToHead withClause
space1
sharedSelectNoParens (Just _with)
selectClause = suffixRec base suffix where
base = asum [
Right <$> selectWithParens,
Left <$> baseSimpleSelect
]
suffix a = Left <$> extensionSimpleSelect a
baseSimpleSelect = asum [
do
keyword "select"
notFollowedBy $ satisfy $ isAlphaNum
endHead
_targeting <- optional (space1 *> targeting)
_intoClause <- optional (space1 *> keyword "into" *> endHead *> space1 *> optTempTableName)
_fromClause <- optional (space1 *> fromClause)
_whereClause <- optional (space1 *> whereClause)
_groupClause <- optional (space1 *> keyphrase "group by" *> endHead *> space1 *> sep1 commaSeparator groupByItem)
_havingClause <- optional (space1 *> keyword "having" *> endHead *> space1 *> aExpr)
_windowClause <- optional (space1 *> keyword "window" *> endHead *> space1 *> sep1 commaSeparator windowDefinition)
return (NormalSimpleSelect _targeting _intoClause _fromClause _whereClause _groupClause _havingClause _windowClause)
,
do
keyword "table"
space1
endHead
TableSimpleSelect <$> relationExpr
,
ValuesSimpleSelect <$> valuesClause
]
extensionSimpleSelect _headSelectClause = do
_op <- space1 *> selectBinOp <* space1
endHead
_allOrDistinct <- optional (allOrDistinct <* space1)
_selectClause <- selectClause
return (BinSimpleSelect _op _headSelectClause _allOrDistinct _selectClause)
allOrDistinct = keyword "all" $> False <|> keyword "distinct" $> True
selectBinOp = asum [
keyword "union" $> UnionSelectBinOp,
keyword "intersect" $> IntersectSelectBinOp,
keyword "except" $> ExceptSelectBinOp
]
valuesClause = do
keyword "values"
space
sep1 commaSeparator $ do
char '('
endHead
space
_a <- sep1 commaSeparator aExpr
space
char ')'
return _a
withClause = label "with clause" $ do
keyword "with"
space1
endHead
_recursive <- option False (True <$ keyword "recursive" <* space1)
_cteList <- sep1 commaSeparator commonTableExpr
return (WithClause _recursive _cteList)
commonTableExpr = label "common table expression" $ do
_name <- colId <* space <* endHead
_nameList <- optional (inParens (sep1 commaSeparator colId) <* space1)
keyword "as"
space1
_materialized <- optional (materialized <* space1)
_stmt <- inParens preparableStmt
return (CommonTableExpr _name _nameList _materialized _stmt)
materialized =
True <$ keyword "materialized" <|>
False <$ keyphrase "not materialized"
targeting = distinct <|> allWithTargetList <|> all <|> normal where
normal = NormalTargeting <$> targetList
allWithTargetList = do
keyword "all"
space1
AllTargeting <$> Just <$> targetList
all = keyword "all" $> AllTargeting Nothing
distinct = do
keyword "distinct"
space1
endHead
_optOn <- optional (onExpressionsClause <* space1)
_targetList <- targetList
return (DistinctTargeting _optOn _targetList)
targetList = sep1 commaSeparator targetEl
targetEl = label "target" $ asum [
do
_expr <- aExpr
asum [
do
space1
asum [
AliasedExprTargetEl _expr <$> (keyword "as" *> space1 *> endHead *> colLabel)
,
ImplicitlyAliasedExprTargetEl _expr <$> ident
]
,
pure (ExprTargetEl _expr)
]
,
AsteriskTargetEl <$ char '*'
]
onExpressionsClause = do
keyword "on"
space1
endHead
inParens (sep1 commaSeparator aExpr)
optTempTableName = asum [
do
a <- asum [
TemporaryOptTempTableName <$ keyword "temporary" <* space1,
TempOptTempTableName <$ keyword "temp" <* space1,
LocalTemporaryOptTempTableName <$ keyphrase "local temporary" <* space1,
LocalTempOptTempTableName <$ keyphrase "local temp" <* space1,
GlobalTemporaryOptTempTableName <$ keyphrase "global temporary" <* space1,
GlobalTempOptTempTableName <$ keyphrase "global temp" <* space1,
UnloggedOptTempTableName <$ keyword "unlogged" <* space1
]
b <- option False (True <$ keyword "table" <* space1)
c <- qualifiedName
return (a b c)
,
do
keyword "table"
space1
endHead
TableOptTempTableName <$> qualifiedName
,
QualifedOptTempTableName <$> qualifiedName
]
groupByItem = asum [
EmptyGroupingSetGroupByItem <$ (char '(' *> space *> char ')'),
RollupGroupByItem <$> (keyword "rollup" *> endHead *> space *> inParens (sep1 commaSeparator aExpr)),
CubeGroupByItem <$> (keyword "cube" *> endHead *> space *> inParens (sep1 commaSeparator aExpr)),
GroupingSetsGroupByItem <$> (keyphrase "grouping sets" *> endHead *> space *> inParens (sep1 commaSeparator groupByItem)),
ExprGroupByItem <$> aExpr
]
windowDefinition = WindowDefinition <$> (colId <* space1 <* keyword "as" <* space1 <* endHead) <*> windowSpecification
windowSpecification = inParens $ asum [
do
a <- frameClause
return (WindowSpecification Nothing Nothing Nothing (Just a))
,
do
a <- sortClause
b <- optional (space1 *> frameClause)
return (WindowSpecification Nothing Nothing (Just a) b)
,
do
a <- partitionByClause
b <- optional (space1 *> sortClause)
c <- optional (space1 *> frameClause)
return (WindowSpecification Nothing (Just a) b c)
,
do
a <- colId
b <- optional (space1 *> partitionByClause)
c <- optional (space1 *> sortClause)
d <- optional (space1 *> frameClause)
return (WindowSpecification (Just a) b c d)
,
pure (WindowSpecification Nothing Nothing Nothing Nothing)
]
partitionByClause = keyphrase "partition by" *> space1 *> endHead *> sep1 commaSeparator aExpr
frameClause = do
a <- frameClauseMode <* space1 <* endHead
b <- frameExtent
c <- optional (space1 *> windowExclusionClause)
return (FrameClause a b c)
frameClauseMode = asum [
RangeFrameClauseMode <$ keyword "range",
RowsFrameClauseMode <$ keyword "rows",
GroupsFrameClauseMode <$ keyword "groups"
]
frameExtent =
BetweenFrameExtent <$> (keyword "between" *> space1 *> endHead *> frameBound <* space1 <* keyword "and" <* space1) <*> frameBound <|>
SingularFrameExtent <$> frameBound
frameBound =
UnboundedPrecedingFrameBound <$ keyphrase "unbounded preceding" <|>
UnboundedFollowingFrameBound <$ keyphrase "unbounded following" <|>
CurrentRowFrameBound <$ keyphrase "current row" <|>
do
a <- aExpr
space1
PrecedingFrameBound a <$ keyword "preceding" <|> FollowingFrameBound a <$ keyword "following"
windowExclusionClause =
CurrentRowWindowExclusionClause <$ keyphrase "exclude current row" <|>
GroupWindowExclusionClause <$ keyphrase "exclude group" <|>
TiesWindowExclusionClause <$ keyphrase "exclude ties" <|>
NoOthersWindowExclusionClause <$ keyphrase "exclude no others"
fromList = sep1 commaSeparator tableRef
fromClause = keyword "from" *> endHead *> space1 *> fromList
tableRef =
label "table reference" $
do
_tr <- nonTrailingTableRef
recur _tr
where
recur _tr =
asum [
do
_tr2 <- wrapToHead (space1 *> trailingTableRef _tr)
endHead
recur _tr2
,
pure _tr
]
nonTrailingTableRef = asum [
lateralTableRef <|>
wrapToHead nonLateralTableRef <|>
relationExprTableRef <|>
joinedTableWithAliasTableRef <|>
inParensJoinedTableTableRef
]
where
relationExprTableRef = do
_relationExpr <- relationExpr
endHead
_optAliasClause <- optional (space1 *> aliasClause)
_optTablesampleClause <- optional (space1 *> tablesampleClause)
return (RelationExprTableRef _relationExpr _optAliasClause _optTablesampleClause)
lateralTableRef = do
keyword "lateral"
space1
endHead
lateralableTableRef True
nonLateralTableRef = lateralableTableRef False
lateralableTableRef _lateral = asum [
do
a <- funcTable
b <- optional (space1 *> funcAliasClause)
return (FuncTableRef _lateral a b)
,
do
_select <- selectWithParens
_optAliasClause <- optional $ space1 *> aliasClause
return (SelectTableRef _lateral _select _optAliasClause)
]
inParensJoinedTableTableRef = JoinTableRef <$> inParensJoinedTable <*> pure Nothing
joinedTableWithAliasTableRef = do
_joinedTable <- wrapToHead (inParens joinedTable)
space1
_alias <- aliasClause
return (JoinTableRef _joinedTable (Just _alias))
trailingTableRef _tableRef =
JoinTableRef <$> trailingJoinedTable _tableRef <*> pure Nothing
relationExpr =
label "relation expression" $
asum
[
do
keyword "only"
space1
_name <- qualifiedName
return (OnlyRelationExpr _name False)
,
inParensWithClause (keyword "only") qualifiedName <&> \ a -> OnlyRelationExpr a True
,
do
_name <- qualifiedName
_asterisk <- asum
[
True <$ (space1 *> char '*'),
pure False
]
return (SimpleRelationExpr _name _asterisk)
]
relationExprOptAlias reservedKeywords = do
a <- relationExpr
b <- optional $ do
space1
b <- trueIfPresent (keyword "as" *> space1)
c <- filteredColId reservedKeywords
return (b, c)
return (RelationExprOptAlias a b)
tablesampleClause = do
keyword "tablesample"
space1
endHead
a <- funcName
space
b <- inParens exprList
c <- optional (space *> repeatableClause)
return (TablesampleClause a b c)
repeatableClause = do
keyword "repeatable"
space
inParens (endHead *> aExpr)
funcTable = asum [
do
keyword "rows"
space1
keyword "from"
space
a <- inParens (endHead *> rowsfromList)
b <- trueIfPresent (space *> optOrdinality)
return (RowsFromFuncTable a b)
,
do
a <- funcExprWindowless
b <- trueIfPresent (space1 *> optOrdinality)
return (FuncExprFuncTable a b)
]
rowsfromItem = do
a <- funcExprWindowless
endHead
b <- optional (space1 *> colDefList)
return (RowsfromItem a b)
rowsfromList = sep1 commaSeparator rowsfromItem
colDefList = keyword "as" *> space *> inParens (endHead *> tableFuncElementList)
optOrdinality = keyword "with" *> space1 *> keyword "ordinality"
tableFuncElementList = sep1 commaSeparator tableFuncElement
tableFuncElement = do
a <- wrapToHead colId
space1
b <- typename
c <- optional (space1 *> collateClause)
return (TableFuncElement a b c)
collateClause = keyword "collate" *> space1 *> endHead *> anyName
funcAliasClause = asum [
do
keyword "as"
asum [
do
space
inParens $ do
endHead
AsFuncAliasClause <$> tableFuncElementList
,
do
space1
a <- colId
asum [
do
space
inParens $ do
endHead
asum [
AsColIdFuncAliasClause a <$> wrapToHead tableFuncElementList,
AliasFuncAliasClause <$> AliasClause True a <$> Just <$> nameList
]
,
pure (AliasFuncAliasClause (AliasClause True a Nothing))
]
]
,
do
a <- colId
asum [
do
space
inParens $ do
endHead
asum [
ColIdFuncAliasClause a <$> wrapToHead tableFuncElementList,
AliasFuncAliasClause <$> AliasClause False a <$> Just <$> nameList
]
,
pure (AliasFuncAliasClause (AliasClause False a Nothing))
]
]
joinedTable =
head >>= tail
where
head =
asum [
do
_tr <- wrapToHead nonTrailingTableRef
space1
trailingJoinedTable _tr
,
inParensJoinedTable
]
tail _jt =
asum [
do
_jt2 <- wrapToHead (space1 *> trailingJoinedTable (JoinTableRef _jt Nothing))
endHead
tail _jt2
,
pure _jt
]
inParensJoinedTable = InParensJoinedTable <$> inParens joinedTable
trailingJoinedTable _tr1 = asum [
do
keyphrase "cross join"
endHead
space1
_tr2 <- nonTrailingTableRef
return (MethJoinedTable CrossJoinMeth _tr1 _tr2)
,
do
_jt <- joinTypedJoin
endHead
space1
_tr2 <- tableRef
space1
_jq <- joinQual
return (MethJoinedTable (QualJoinMeth _jt _jq) _tr1 _tr2)
,
do
keyword "natural"
endHead
space1
_jt <- joinTypedJoin
space1
_tr2 <- nonTrailingTableRef
return (MethJoinedTable (NaturalJoinMeth _jt) _tr1 _tr2)
]
where
joinTypedJoin =
Just <$> (joinType <* endHead <* space1 <* keyword "join") <|>
Nothing <$ keyword "join"
joinType = asum [
do
keyword "full"
endHead
_outer <- outerAfterSpace
return (FullJoinType _outer)
,
do
keyword "left"
endHead
_outer <- outerAfterSpace
return (LeftJoinType _outer)
,
do
keyword "right"
endHead
_outer <- outerAfterSpace
return (RightJoinType _outer)
,
keyword "inner" $> InnerJoinType
]
where
outerAfterSpace = (space1 *> keyword "outer") $> True <|> pure False
joinQual = asum [
keyword "using" *> space1 *> inParens (sep1 commaSeparator colId) <&> UsingJoinQual
,
keyword "on" *> space1 *> aExpr <&> OnJoinQual
]
aliasClause = do
(_as, _alias) <- (True,) <$> (keyword "as" *> space1 *> endHead *> colId) <|> (False,) <$> colId
_columnAliases <- optional (space1 *> inParens (sep1 commaSeparator colId))
return (AliasClause _as _alias _columnAliases)
whereClause = keyword "where" *> space1 *> endHead *> aExpr
whereOrCurrentClause = do
keyword "where"
space1
endHead
asum [
do
keyword "current"
space1
keyword "of"
space1
endHead
a <- cursorName
return (CursorWhereOrCurrentClause a)
,
ExprWhereOrCurrentClause <$> aExpr
]
sortClause = do
keyphrase "order by"
endHead
space1
a <- sep1 commaSeparator sortBy
return a
sortBy = do
a <- filteredAExpr ["using", "asc", "desc", "nulls"]
asum [
do
space1
keyword "using"
space1
endHead
b <- qualAllOp
c <- optional (space1 *> nullsOrder)
return (UsingSortBy a b c)
,
do
b <- optional (space1 *> ascDesc)
c <- optional (space1 *> nullsOrder)
return (AscDescSortBy a b c)
]
exprList = sep1 commaSeparator aExpr
exprListInParens = inParens exprList
aExpr = customizedAExpr cExpr
filteredAExpr = customizedAExpr . customizedCExpr . filteredColumnref
customizedAExpr cExpr = suffixRec base suffix where
aExpr = customizedAExpr cExpr
base = asum [
DefaultAExpr <$ keyword "default",
UniqueAExpr <$> (keyword "unique" *> space1 *> selectWithParens),
OverlapsAExpr <$> wrapToHead row <*> (space1 *> keyword "overlaps" *> space1 *> endHead *> row),
qualOpExpr aExpr PrefixQualOpAExpr,
PlusAExpr <$> plusedExpr aExpr,
MinusAExpr <$> minusedExpr aExpr,
NotAExpr <$> (keyword "not" *> space1 *> aExpr),
CExprAExpr <$> cExpr
]
suffix a = asum [
do
space1
b <- wrapToHead subqueryOp
space1
c <- wrapToHead subType
space
d <- Left <$> wrapToHead selectWithParens <|> Right <$> inParens aExpr
return (SubqueryAExpr a b c d)
,
typecastExpr a TypecastAExpr
,
CollateAExpr a <$> (space1 *> keyword "collate" *> space1 *> endHead *> anyName)
,
AtTimeZoneAExpr a <$> (space1 *> keyphrase "at time zone" *> space1 *> endHead *> aExpr)
,
symbolicBinOpExpr a aExpr SymbolicBinOpAExpr
,
SuffixQualOpAExpr a <$> (space *> qualOp)
,
AndAExpr a <$> (space1 *> keyword "and" *> space1 *> endHead *> aExpr)
,
OrAExpr a <$> (space1 *> keyword "or" *> space1 *> endHead *> aExpr)
,
do
space1
b <- trueIfPresent (keyword "not" *> space1)
c <- asum [
LikeVerbalExprBinOp <$ keyword "like",
IlikeVerbalExprBinOp <$ keyword "ilike",
SimilarToVerbalExprBinOp <$ keyphrase "similar to"
]
space1
endHead
d <- aExpr
e <- optional (space1 *> keyword "escape" *> space1 *> endHead *> aExpr)
return (VerbalExprBinOpAExpr a b c d e)
,
do
space1
keyword "is"
space1
endHead
b <- trueIfPresent (keyword "not" *> space1)
c <- asum [
NullAExprReversableOp <$ keyword "null",
TrueAExprReversableOp <$ keyword "true",
FalseAExprReversableOp <$ keyword "false",
UnknownAExprReversableOp <$ keyword "unknown",
DistinctFromAExprReversableOp <$> (keyword "distinct" *> space1 *> keyword "from" *> space1 *> endHead *> aExpr),
OfAExprReversableOp <$> (keyword "of" *> space1 *> endHead *> inParens typeList),
DocumentAExprReversableOp <$ keyword "document"
]
return (ReversableOpAExpr a b c)
,
do
space1
b <- trueIfPresent (keyword "not" *> space1)
keyword "between"
space1
endHead
c <- asum [
BetweenSymmetricAExprReversableOp <$ (keyword "symmetric" *> space1),
BetweenAExprReversableOp True <$ (keyword "asymmetric" *> space1),
pure (BetweenAExprReversableOp False)
]
d <- bExpr
space1
keyword "and"
space1
e <- aExpr
return (ReversableOpAExpr a b (c d e))
,
do
space1
b <- trueIfPresent (keyword "not" *> space1)
keyword "in"
space
c <- InAExprReversableOp <$> inExpr
return (ReversableOpAExpr a b c)
,
IsnullAExpr a <$ (space1 *> keyword "isnull")
,
NotnullAExpr a <$ (space1 *> keyword "notnull")
]
bExpr = customizedBExpr cExpr
customizedBExpr cExpr = suffixRec base suffix where
aExpr = customizedAExpr cExpr
bExpr = customizedBExpr cExpr
base = asum [
qualOpExpr bExpr QualOpBExpr,
PlusBExpr <$> plusedExpr bExpr,
MinusBExpr <$> minusedExpr bExpr,
CExprBExpr <$> cExpr
]
suffix a = asum [
typecastExpr a TypecastBExpr,
symbolicBinOpExpr a bExpr SymbolicBinOpBExpr,
do
space1
keyword "is"
space1
endHead
b <- trueIfPresent (keyword "not" *> space1)
c <- asum [
DistinctFromBExprIsOp <$> (keyphrase "distinct from" *> space1 *> endHead *> bExpr),
OfBExprIsOp <$> (keyword "of" *> space1 *> endHead *> inParens typeList),
DocumentBExprIsOp <$ keyword "document"
]
return (IsOpBExpr a b c)
]
cExpr = customizedCExpr columnref
customizedCExpr columnref = asum [
ParamCExpr <$> (char '$' *> decimal <* endHead) <*> optional (space *> indirection)
,
CaseCExpr <$> caseExpr
,
ImplicitRowCExpr <$> implicitRow
,
ExplicitRowCExpr <$> explicitRow
,
inParensWithClause (keyword "grouping") (GroupingCExpr <$> sep1 commaSeparator aExpr)
,
keyword "exists" *> space *> (ExistsCExpr <$> selectWithParens)
,
do
keyword "array"
space
join $ asum [
fmap (fmap (ArrayCExpr . Right)) arrayExprCont,
fmap (fmap (ArrayCExpr . Left) . pure) selectWithParens
]
,
do
a <- wrapToHead selectWithParens
endHead
b <- optional (space *> indirection)
return (SelectWithParensCExpr a b)
,
InParensCExpr <$> (inParens aExpr <* endHead) <*> optional (space *> indirection)
,
AexprConstCExpr <$> wrapToHead aexprConst
,
FuncCExpr <$> funcExpr
,
ColumnrefCExpr <$> columnref
]
subqueryOp = asum [
AnySubqueryOp <$> (keyword "operator" *> space *> endHead *> inParens anyOperator)
,
do
a <- trueIfPresent (keyword "not" *> space1)
LikeSubqueryOp a <$ keyword "like" <|> IlikeSubqueryOp a <$ keyword "ilike"
,
AllSubqueryOp <$> allOp
]
subType = asum [
AnySubType <$ keyword "any",
SomeSubType <$ keyword "some",
AllSubType <$ keyword "all"
]
inExpr = SelectInExpr <$> wrapToHead selectWithParens <|> ExprListInExpr <$> inParens exprList
symbolicBinOpExpr _a _bParser _constr = do
_binOp <- label "binary operator" (space *> wrapToHead symbolicExprBinOp <* space)
_b <- _bParser
return (_constr _a _binOp _b)
typecastExpr :: a -> (a -> Typename -> a) -> HeadedParsec Void Text a
typecastExpr _prefix _constr = do
space
string "::"
endHead
space
_type <- typename
return (_constr _prefix _type)
plusedExpr expr = char '+' *> space *> expr
minusedExpr expr = char '-' *> space *> expr
qualOpExpr expr constr = constr <$> wrapToHead qualOp <*> (space *> expr)
row = ExplicitRowRow <$> explicitRow <|> ImplicitRowRow <$> implicitRow
explicitRow = keyword "row" *> space *> inParens (optional exprList)
implicitRow = inParens $ do
a <- wrapToHead aExpr
commaSeparator
b <- exprList
return $ case NonEmpty.consAndUnsnoc a b of
(c, d) -> ImplicitRow c d
arrayExprCont = inBracketsCont $ asum [
ArrayExprListArrayExpr <$> sep1 commaSeparator (join arrayExprCont),
ExprListArrayExpr <$> exprList,
pure EmptyArrayExpr
]
caseExpr = label "case expression" $ do
keyword "case"
space1
endHead
_arg <- optional (aExpr <* space1)
_whenClauses <- sep1 space1 whenClause
space1
_default <- optional elseClause
keyword "end"
pure $ CaseExpr _arg _whenClauses _default
whenClause = do
keyword "when"
space1
endHead
_a <- aExpr
space1
keyword "then"
space1
_b <- aExpr
return (WhenClause _a _b)
elseClause = do
keyword "else"
space1
endHead
a <- aExpr
space1
return a
funcExpr = asum [
SubexprFuncExpr <$> funcExprCommonSubexpr,
do
a <- funcApplication
endHead
b <- optional (space1 *> withinGroupClause)
c <- optional (space1 *> filterClause)
d <- optional (space1 *> overClause)
return (ApplicationFuncExpr a b c d)
]
funcExprWindowless = asum [
CommonSubexprFuncExprWindowless <$> funcExprCommonSubexpr,
ApplicationFuncExprWindowless <$> funcApplication
]
withinGroupClause = do
keyphrase "within group"
endHead
space
inParens sortClause
filterClause = do
keyword "filter"
endHead
space
inParens (keyword "where" *> space1 *> aExpr)
overClause = do
keyword "over"
space1
endHead
asum [
WindowOverClause <$> windowSpecification,
ColIdOverClause <$> colId
]
funcExprCommonSubexpr = asum [
CollationForFuncExprCommonSubexpr <$> (inParensWithClause (keyphrase "collation for") aExpr)
,
CurrentDateFuncExprCommonSubexpr <$ keyword "current_date"
,
CurrentTimestampFuncExprCommonSubexpr <$> labeledIconst "current_timestamp"
,
CurrentTimeFuncExprCommonSubexpr <$> labeledIconst "current_time"
,
LocalTimestampFuncExprCommonSubexpr <$> labeledIconst "localtimestamp"
,
LocalTimeFuncExprCommonSubexpr <$> labeledIconst "localtime"
,
CurrentRoleFuncExprCommonSubexpr <$ keyword "current_role"
,
CurrentUserFuncExprCommonSubexpr <$ keyword "current_user"
,
SessionUserFuncExprCommonSubexpr <$ keyword "session_user"
,
UserFuncExprCommonSubexpr <$ keyword "user"
,
CurrentCatalogFuncExprCommonSubexpr <$ keyword "current_catalog"
,
CurrentSchemaFuncExprCommonSubexpr <$ keyword "current_schema"
,
inParensWithClause (keyword "cast") (CastFuncExprCommonSubexpr <$> aExpr <*> (space1 *> keyword "as" *> space1 *> typename))
,
inParensWithClause (keyword "extract") (ExtractFuncExprCommonSubexpr <$> optional extractList)
,
inParensWithClause (keyword "overlay") (OverlayFuncExprCommonSubexpr <$> overlayList)
,
inParensWithClause (keyword "position") (PositionFuncExprCommonSubexpr <$> optional positionList)
,
inParensWithClause (keyword "substring") (SubstringFuncExprCommonSubexpr <$> optional substrList)
,
inParensWithClause (keyword "treat") (TreatFuncExprCommonSubexpr <$> aExpr <*> (space1 *> keyword "as" *> space1 *> typename))
,
inParensWithClause (keyword "trim") (TrimFuncExprCommonSubexpr <$> optional (trimModifier <* space1) <*> trimList)
,
inParensWithClause (keyword "nullif") (NullIfFuncExprCommonSubexpr <$> aExpr <*> (commaSeparator *> aExpr))
,
inParensWithClause (keyword "coalesce") (CoalesceFuncExprCommonSubexpr <$> exprList)
,
inParensWithClause (keyword "greatest") (GreatestFuncExprCommonSubexpr <$> exprList)
,
inParensWithClause (keyword "least") (LeastFuncExprCommonSubexpr <$> exprList)
]
where
labeledIconst _label = keyword _label *> endHead *> optional (space *> inParens iconst)
extractList = ExtractList <$> extractArg <*> (space1 *> keyword "from" *> space1 *> aExpr)
extractArg = asum [
YearExtractArg <$ keyword "year",
MonthExtractArg <$ keyword "month",
DayExtractArg <$ keyword "day",
HourExtractArg <$ keyword "hour",
MinuteExtractArg <$ keyword "minute",
SecondExtractArg <$ keyword "second",
SconstExtractArg <$> sconst,
IdentExtractArg <$> ident
]
overlayList = do
a <- aExpr
space1
b <- overlayPlacing
space1
c <- substrFrom
d <- optional (space1 *> substrFor)
return (OverlayList a b c d)
overlayPlacing = keyword "placing" *> space1 *> endHead *> aExpr
positionList = PositionList <$> bExpr <*> (space1 *> keyword "in" *> space1 *> bExpr)
substrList = asum [
ExprSubstrList <$> wrapToHead aExpr <*> (space1 *> substrListFromFor),
ExprListSubstrList <$> exprList
]
substrListFromFor = asum [
do
a <- substrFrom
asum [
do
b <- space1 *> substrFor
return (FromForSubstrListFromFor a b)
,
return (FromSubstrListFromFor a)
]
,
do
a <- substrFor
asum [
do
b <- space1 *> substrFrom
return (ForFromSubstrListFromFor a b)
,
return (ForSubstrListFromFor a)
]
]
substrFrom = keyword "from" *> space1 *> endHead *> aExpr
substrFor = keyword "for" *> space1 *> endHead *> aExpr
trimModifier =
BothTrimModifier <$ keyword "both" <|>
LeadingTrimModifier <$ keyword "leading" <|>
TrailingTrimModifier <$ keyword "trailing"
trimList = asum [
ExprFromExprListTrimList <$> wrapToHead aExpr <*> (space1 *> keyword "from" *> space1 *> endHead *> exprList)
,
FromExprListTrimList <$> (keyword "from" *> space1 *> endHead *> exprList)
,
ExprListTrimList <$> exprList
]
funcApplication = inParensWithLabel FuncApplication funcName (optional funcApplicationParams)
funcApplicationParams =
asum
[
starFuncApplicationParams,
listVariadicFuncApplicationParams,
singleVariadicFuncApplicationParams,
normalFuncApplicationParams
]
normalFuncApplicationParams = do
_optAllOrDistinct <- optional (allOrDistinct <* space1)
_argList <- sep1 commaSeparator funcArgExpr
endHead
_optSortClause <- optional (space1 *> sortClause)
return (NormalFuncApplicationParams _optAllOrDistinct _argList _optSortClause)
singleVariadicFuncApplicationParams = do
keyword "variadic"
space1
endHead
_arg <- funcArgExpr
_optSortClause <- optional (space1 *> sortClause)
return (VariadicFuncApplicationParams Nothing _arg _optSortClause)
listVariadicFuncApplicationParams = do
(_argList, _) <- wrapToHead $ sepEnd1 commaSeparator (keyword "variadic" <* space1) funcArgExpr
endHead
_arg <- funcArgExpr
_optSortClause <- optional (space1 *> sortClause)
return (VariadicFuncApplicationParams (Just _argList) _arg _optSortClause)
starFuncApplicationParams = space *> char '*' *> endHead *> space $> StarFuncApplicationParams
funcArgExpr = asum [
do
a <- wrapToHead typeFunctionName
space
asum [
do
string ":="
endHead
b <- space *> aExpr
return (ColonEqualsFuncArgExpr a b)
,
do
string "=>"
endHead
b <- space *> aExpr
return (EqualsGreaterFuncArgExpr a b)
]
,
ExprFuncArgExpr <$> aExpr
]
symbolicExprBinOp =
QualSymbolicExprBinOp <$> qualOp <|>
MathSymbolicExprBinOp <$> mathOp
lexicalExprBinOp = asum $ fmap keyphrase $ ["and", "or", "is distinct from", "is not distinct from"]
qualOp = asum [
OpQualOp <$> op,
OperatorQualOp <$> inParensWithClause (keyword "operator") anyOperator
]
qualAllOp = asum [
AnyQualAllOp <$> (keyword "operator" *> space *> inParens (endHead *> anyOperator)),
AllQualAllOp <$> allOp
]
op = do
a <- takeWhile1P Nothing Predicate.opChar
case Validation.op a of
Nothing -> return a
Just err -> fail (Text.unpack err)
anyOperator = asum [
AllOpAnyOperator <$> allOp,
QualifiedAnyOperator <$> colId <*> (space *> char '.' *> space *> anyOperator)
]
allOp = asum [
OpAllOp <$> op,
MathAllOp <$> mathOp
]
mathOp = asum [
ArrowLeftArrowRightMathOp <$ string' "<>",
GreaterEqualsMathOp <$ string' ">=",
ExclamationEqualsMathOp <$ string' "!=",
LessEqualsMathOp <$ string' "<=",
PlusMathOp <$ char '+',
MinusMathOp <$ char '-',
AsteriskMathOp <$ char '*',
SlashMathOp <$ char '/',
PercentMathOp <$ char '%',
ArrowUpMathOp <$ char '^',
ArrowLeftMathOp <$ char '<',
ArrowRightMathOp <$ char '>',
EqualsMathOp <$ char '='
]
aexprConst = asum [
do
keyword "interval"
space1
endHead
a <- asum [
do
a <- sconst
endHead
b <- optional (space1 *> interval)
return (StringIntervalAexprConst a b)
,
do
a <- inParens iconst
space1
endHead
b <- sconst
return (IntIntervalAexprConst a b)
]
return a
,
do
a <- constTypename
space1
endHead
b <- sconst
return (ConstTypenameAexprConst a b)
,
BoolAexprConst True <$ keyword "true"
,
BoolAexprConst False <$ keyword "false"
,
NullAexprConst <$ keyword "null" <* parse (Megaparsec.notFollowedBy MegaparsecChar.alphaNumChar)
,
either IAexprConst FAexprConst <$> iconstOrFconst
,
SAexprConst <$> sconst
,
label "bit literal" $ do
string' "b'"
endHead
a <- takeWhile1P (Just "0 or 1") (\ b -> b == '0' || b == '1')
char '\''
return (BAexprConst a)
,
label "hex literal" $ do
string' "x'"
endHead
a <- takeWhile1P (Just "Hex digit") Predicate.hexDigit
char '\''
return (XAexprConst a)
,
wrapToHead $ do
a <- funcName
space
char '('
space
b <- sep1 commaSeparator funcArgExpr
c <- optional (space1 *> sortClause)
space
char ')'
space1
d <- sconst
return (FuncAexprConst a (Just (FuncConstArgs b c)) d)
,
FuncAexprConst <$> (wrapToHead funcName <* space1) <*> pure Nothing <*> sconst
]
iconstOrFconst = Right <$> fconst <|> Left <$> iconst
iconst = decimal
fconst = float
sconst = quotedString '\''
constTypename = asum [
NumericConstTypename <$> numeric,
ConstBitConstTypename <$> constBit,
ConstCharacterConstTypename <$> constCharacter,
ConstDatetimeConstTypename <$> constDatetime
]
numeric = asum [
IntegerNumeric <$ keyword "integer",
IntNumeric <$ keyword "int",
SmallintNumeric <$ keyword "smallint",
BigintNumeric <$ keyword "bigint",
RealNumeric <$ keyword "real",
FloatNumeric <$> (keyword "float" *> endHead *> optional (space *> inParens iconst)),
DoublePrecisionNumeric <$ keyphrase "double precision",
DecimalNumeric <$> (keyword "decimal" *> endHead *> optional (space *> exprListInParens)),
DecNumeric <$> (keyword "dec" *> endHead *> optional (space *> exprListInParens)),
NumericNumeric <$> (keyword "numeric" *> endHead *> optional (space *> exprListInParens)),
BooleanNumeric <$ keyword "boolean"
]
bit = do
keyword "bit"
a <- option False (True <$ space1 <* keyword "varying")
b <- optional (space1 *> exprListInParens)
return (Bit a b)
constBit = bit
constCharacter = ConstCharacter <$> (character <* endHead) <*> optional (space *> inParens iconst)
character = asum [
CharacterCharacter <$> (keyword "character" *> optVaryingAfterSpace),
CharCharacter <$> (keyword "char" *> optVaryingAfterSpace),
VarcharCharacter <$ keyword "varchar",
NationalCharacterCharacter <$> (keyphrase "national character" *> optVaryingAfterSpace),
NationalCharCharacter <$> (keyphrase "national char" *> optVaryingAfterSpace),
NcharCharacter <$> (keyword "nchar" *> optVaryingAfterSpace)
]
where
optVaryingAfterSpace = True <$ space1 <* keyword "varying" <|> pure False
constDatetime = asum [
do
keyword "timestamp"
a <- optional (space1 *> inParens iconst)
b <- optional (space1 *> timezone)
return (TimestampConstDatetime a b)
,
do
keyword "time"
a <- optional (space1 *> inParens iconst)
b <- optional (space1 *> timezone)
return (TimeConstDatetime a b)
]
timezone = asum [
False <$ keyphrase "with time zone",
True <$ keyphrase "without time zone"
]
interval = asum [
YearToMonthInterval <$ keyphrase "year to month",
DayToHourInterval <$ keyphrase "day to hour",
DayToMinuteInterval <$ keyphrase "day to minute",
DayToSecondInterval <$> (keyphrase "day to" *> space1 *> endHead *> intervalSecond),
HourToMinuteInterval <$ keyphrase "hour to minute",
HourToSecondInterval <$> (keyphrase "hour to" *> space1 *> endHead *> intervalSecond),
MinuteToSecondInterval <$> (keyphrase "minute to" *> space1 *> endHead *> intervalSecond),
YearInterval <$ keyword "year",
MonthInterval <$ keyword "month",
DayInterval <$ keyword "day",
HourInterval <$ keyword "hour",
MinuteInterval <$ keyword "minute",
SecondInterval <$> intervalSecond
]
intervalSecond = do
keyword "second"
a <- optional (space *> inParens iconst)
return a
selectLimit =
asum
[
do
_a <- limitClause
LimitOffsetSelectLimit _a <$> (space1 *> offsetClause) <|> pure (LimitSelectLimit _a)
,
do
_a <- offsetClause
OffsetLimitSelectLimit _a <$> (space1 *> limitClause) <|> pure (OffsetSelectLimit _a)
]
limitClause =
(do
keyword "limit"
endHead
space1
_a <- selectLimitValue
_b <- optional $ do
commaSeparator
aExpr
return (LimitLimitClause _a _b)
) <|>
(do
keyword "fetch"
endHead
space1
_a <- firstOrNext
space1
asum [
do
_b <- rowOrRows
space1
keyword "only"
return (FetchOnlyLimitClause _a Nothing _b)
,
do
_b <- selectFetchFirstValue
space1
_c <- rowOrRows
space1
keyword "only"
return (FetchOnlyLimitClause _a (Just _b) _c)
]
)
offsetClause = do
keyword "offset"
endHead
space1
offsetClauseParams
offsetClauseParams =
FetchFirstOffsetClause <$> wrapToHead selectFetchFirstValue <*> (space1 *> rowOrRows) <|>
ExprOffsetClause <$> aExpr
selectLimitValue =
AllSelectLimitValue <$ keyword "all" <|>
ExprSelectLimitValue <$> aExpr
rowOrRows =
True <$ keyword "rows" <|>
False <$ keyword "row"
firstOrNext =
False <$ keyword "first" <|>
True <$ keyword "next"
selectFetchFirstValue =
ExprSelectFetchFirstValue <$> cExpr <|>
NumSelectFetchFirstValue <$> (plusOrMinus <* endHead <* space) <*> iconstOrFconst
plusOrMinus = False <$ char '+' <|> True <$ char '-'
forLockingClause = readOnly <|> items where
readOnly = ReadOnlyForLockingClause <$ keyphrase "for read only"
items = ItemsForLockingClause <$> sep1 space1 forLockingItem
forLockingItem = do
_strength <- forLockingStrength
_rels <- optional $ space1 *> keyword "of" *> space1 *> endHead *> sep1 commaSeparator qualifiedName
_nowaitOrSkip <- optional (space1 *> nowaitOrSkip)
return (ForLockingItem _strength _rels _nowaitOrSkip)
forLockingStrength =
UpdateForLockingStrength <$ keyphrase "for update" <|>
NoKeyUpdateForLockingStrength <$ keyphrase "for no key update" <|>
ShareForLockingStrength <$ keyphrase "for share" <|>
KeyForLockingStrength <$ keyphrase "for key share"
nowaitOrSkip = False <$ keyword "nowait" <|> True <$ keyphrase "skip locked"
quotedName = filter (const "Empty name") (not . Text.null) (quotedString '"') & fmap QuotedIdent
ident = quotedName <|> keywordNameByPredicate (not . Predicate.keyword)
{-# NOINLINE colId #-}
colId = label "identifier" $
ident <|> keywordNameFromSet (KeywordSet.unreservedKeyword <> KeywordSet.colNameKeyword)
{-# NOINLINE filteredColId #-}
filteredColId = let
_originalSet = KeywordSet.unreservedKeyword <> KeywordSet.colNameKeyword
_filteredSet = foldr HashSet.delete _originalSet
in \ _reservedKeywords -> label "identifier" $ ident <|> keywordNameFromSet (_filteredSet _reservedKeywords)
colLabel = label "column label" $
keywordNameFromSet KeywordSet.keyword <|> ident
qualifiedName =
IndirectedQualifiedName <$> wrapToHead colId <*> (space *> indirection) <|>
SimpleQualifiedName <$> colId
columnref = customizedColumnref colId
filteredColumnref _keywords = customizedColumnref (filteredColId _keywords)
customizedColumnref colId = do
a <- wrapToHead colId
endHead
b <- optional (space *> indirection)
return (Columnref a b)
anyName = customizedAnyName colId
filteredAnyName _keywords = customizedAnyName (filteredColId _keywords)
customizedAnyName colId = do
a <- wrapToHead colId
endHead
b <- optional (space *> attrs)
return (AnyName a b)
name = colId
nameList = sep1 commaSeparator name
cursorName = name
funcName =
IndirectedFuncName <$> wrapToHead colId <*> (space *> indirection) <|>
TypeFuncName <$> typeFunctionName
typeFunctionName =
keywordNameFromSet KeywordSet.typeFunctionName <|>
ident
indirection = some indirectionEl
indirectionEl =
asum
[
do
char '.'
endHead
space
AllIndirectionEl <$ char '*' <|> AttrNameIndirectionEl <$> attrName
,
do
char '['
endHead
space
_a <- asum [
do
char ':'
endHead
space
_b <- optional aExpr
return (SliceIndirectionEl Nothing _b)
,
do
_a <- aExpr
asum [
do
space
char ':'
space
_b <- optional aExpr
return (SliceIndirectionEl (Just _a) _b)
,
return (ExprIndirectionEl _a)
]
]
space
char ']'
return _a
]
attrName = colLabel
keywordNameFromSet _set = keywordNameByPredicate (Predicate.inSet _set)
keywordNameByPredicate _predicate =
fmap UnquotedIdent $
filter
(\ a -> "Reserved keyword " <> show a <> " used as an identifier. If that's what you intend, you have to wrap it in double quotes.")
_predicate
anyKeyword
anyKeyword = parse $ Megaparsec.label "keyword" $ do
_firstChar <- Megaparsec.satisfy Predicate.firstIdentifierChar
_remainder <- Megaparsec.takeWhileP Nothing Predicate.notFirstIdentifierChar
return (Text.toLower (Text.cons _firstChar _remainder))
keyword a = mfilter (a ==) anyKeyword
keyphrase a =
Text.words a &
fmap (void . MegaparsecChar.string') &
intersperse MegaparsecChar.space1 &
sequence_ &
(<* Megaparsec.notFollowedBy (Megaparsec.satisfy Predicate.notFirstIdentifierChar)) &
fmap (const (Text.toUpper a)) &
Megaparsec.label (show a) &
parse &
(<* endHead)
typeList = sep1 commaSeparator typename
typename =
do
a <- option False (keyword "setof" *> space1 $> True)
b <- simpleTypename
endHead
c <- trueIfPresent (space *> char '?')
asum [
do
space1
keyword "array"
endHead
d <- optional (space *> inBrackets iconst)
e <- trueIfPresent (space *> char '?')
return (Typename a b c (Just (ExplicitTypenameArrayDimensions d, e)))
,
do
space1
d <- arrayBounds
endHead
e <- trueIfPresent (space *> char '?')
return (Typename a b c (Just (BoundsTypenameArrayDimensions d, e)))
,
return (Typename a b c Nothing)
]
arrayBounds = sep1 space (inBrackets (optional iconst))
simpleTypename = asum $ [
do
keyword "interval"
endHead
asum [
ConstIntervalSimpleTypename <$> Right <$> (space *> inParens iconst),
ConstIntervalSimpleTypename <$> Left <$> optional (space *> interval)
],
ConstDatetimeSimpleTypename <$> constDatetime,
NumericSimpleTypename <$> numeric,
BitSimpleTypename <$> bit,
CharacterSimpleTypename <$> character,
GenericTypeSimpleTypename <$> genericType
]
genericType = do
a <- typeFunctionName
endHead
b <- optional (space *> attrs)
c <- optional (space1 *> typeModifiers)
return (GenericType a b c)
attrs = some (char '.' *> endHead *> space *> attrName)
typeModifiers = inParens exprList
indexParams = sep1 commaSeparator indexElem
indexElem = IndexElem <$>
(indexElemDef <* endHead) <*>
optional (space1 *> collate) <*>
optional (space1 *> class_) <*>
optional (space1 *> ascDesc) <*>
optional (space1 *> nullsOrder)
indexElemDef =
ExprIndexElemDef <$> inParens aExpr <|>
FuncIndexElemDef <$> funcExprWindowless <|>
IdIndexElemDef <$> colId
collate = keyword "collate" *> space1 *> endHead *> anyName
class_ = filteredAnyName ["asc", "desc", "nulls"]
ascDesc = keyword "asc" $> AscAscDesc <|> keyword "desc" $> DescAscDesc
nullsOrder = keyword "nulls" *> space1 *> endHead *> (FirstNullsOrder <$ keyword "first" <|> LastNullsOrder <$ keyword "last")