postgresql-syntax-0.5.0.0: library-internal/PostgresqlSyntax/Ast/SelectNoParens.hs
module PostgresqlSyntax.Ast.SelectNoParens
( SelectNoParens (..),
unparenthesizedSelectNoParensParser,
afterSelectWithParensClauseParser,
refineToSelectWithParens,
)
where
import qualified HeadedMegaparsec as Parser
import PostgresqlSyntax.Ast.ForLockingClause
import PostgresqlSyntax.Ast.SelectClause
import PostgresqlSyntax.Ast.SelectLimit
import {-# SOURCE #-} PostgresqlSyntax.Ast.SelectWithParens (SelectWithParens)
import {-# SOURCE #-} qualified PostgresqlSyntax.Ast.SimpleSelect as SimpleSelect
import PostgresqlSyntax.Ast.SortClause
import {-# SOURCE #-} PostgresqlSyntax.Ast.WithClause (WithClause)
import qualified PostgresqlSyntax.Helpers.Gens as Gens
import qualified PostgresqlSyntax.Helpers.Parsers as Parsers
import qualified PostgresqlSyntax.Helpers.TextBuilders as TextBuilders
import PostgresqlSyntax.IsAst
import PostgresqlSyntax.Prelude
import PostgresqlSyntax.Settings (Settings)
import qualified Test.QuickCheck as Qc
-- |
-- Covers the following cases:
--
-- @
-- select_no_parens:
-- | simple_select
-- | select_clause sort_clause
-- | select_clause opt_sort_clause for_locking_clause opt_select_limit
-- | select_clause opt_sort_clause select_limit opt_for_locking_clause
-- | with_clause select_clause
-- | with_clause select_clause sort_clause
-- | with_clause select_clause opt_sort_clause for_locking_clause opt_select_limit
-- | with_clause select_clause opt_sort_clause select_limit opt_for_locking_clause
-- @
data SelectNoParens
= SelectNoParens (Maybe WithClause) SelectClause (Maybe SortClause) (Maybe SelectLimit) (Maybe ForLockingClause)
deriving (Show, Generic, Eq, Ord, Data)
instance IsAst SelectNoParens where
toTextBuilder settings (SelectNoParens a b c d e) =
TextBuilders.optLexemes
[ fmap (toTextBuilder settings) a,
Just (toTextBuilder settings b),
fmap (toTextBuilder settings) c,
fmap (toTextBuilder settings) d,
fmap (toTextBuilder settings) e
]
parser settings = withSelectNoParens <|> simpleSelectNoParens
where
simpleSelectNoParens = sharedSelectNoParens settings Nothing
withSelectNoParens = do
with <- Parser.wrapToHead (parser settings)
Parsers.space1
sharedSelectNoParens settings (Just with)
sharedSelectNoParens :: Settings -> Maybe WithClause -> Parser SelectNoParens
sharedSelectNoParens settings with = SimpleSelect.selectClauseBase settings >>= selectNoParensAfterClauseParser settings with
-- |
-- 'PostgresqlSyntax.Ast.SelectNoParens.parser' restricted to the forms
-- that do not begin with @(@ — see the boot-exposed signature's doc.
unparenthesizedSelectNoParensParser :: Settings -> Parser SelectNoParens
unparenthesizedSelectNoParensParser settings =
withSelectNoParens
<|> (SimpleSelect.baseSimpleSelect settings >>= selectNoParensAfterClauseParser settings Nothing . SimpleSelectSelectClause)
where
withSelectNoParens = do
with <- Parser.wrapToHead (parser settings)
Parsers.space1
sharedSelectNoParens settings (Just with)
selectNoParensAfterClauseParser :: Settings -> Maybe WithClause -> SelectClause -> Parser SelectNoParens
selectNoParensAfterClauseParser settings with clauseBase = do
select <- SimpleSelect.extendSelectClause settings clauseBase
sort <- optional (Parsers.space1 *> parser settings)
(limit, forLocking) <- limitFirst <|> forLockingFirst <|> pure (Nothing, Nothing)
return (SelectNoParens with select sort limit forLocking)
where
limitFirst = do
limit <- Parsers.space1 *> parser settings
forLocking <- optional (Parsers.space1 *> parser settings)
pure (Just limit, forLocking)
forLockingFirst = do
forLocking <- Parsers.space1 *> parser settings
limit <- optional (Parsers.space1 *> parser settings)
pure (limit, Just forLocking)
-- |
-- Parses the remainder of a @select_no_parens@ whose leading clause is
-- already known to be a parenthesized select (@a@), then decides — per the
-- \"Canonical shape\" rule — whether the result collapses back down to just
-- that same parenthesized select (@Left@) or is a genuine
-- @SelectNoParens@ wrapping it (@Right@). Needed by
-- "PostgresqlSyntax.Ast.SelectWithParens", which can't pattern-match this
-- module's own 'SelectNoParens' constructor across the hub boundary.
afterSelectWithParensClauseParser :: Settings -> SelectWithParens -> Parser (Either SelectWithParens SelectNoParens)
afterSelectWithParensClauseParser settings a = do
b <- selectNoParensAfterClauseParser settings Nothing (WithParensSelectClause a)
return $ case refineToSelectWithParens b of
Just c -> Left c
Nothing -> Right b
-- |
-- If a 'SelectNoParens' is merely a trivial wrapper around a single
-- parenthesized select — no with-clause, sort, limit or locking clause of
-- its own — returns the wrapped 'SelectWithParens'. Used by
-- "PostgresqlSyntax.Ast.SelectWithParens" to canonicalize such wrappers
-- into its @WithParensSelectWithParens@ shape — see its \"Canonical
-- shape\" doc.
refineToSelectWithParens :: SelectNoParens -> Maybe SelectWithParens
refineToSelectWithParens = \case
SelectNoParens Nothing (WithParensSelectClause c) Nothing Nothing Nothing -> Just c
_ -> Nothing
instance Qc.Arbitrary SelectNoParens where
shrink = Qc.genericShrink
arbitrary =
sized $ \size ->
if size <= 1
then do
selectClause <- Qc.arbitrary
return (SelectNoParens Nothing selectClause Nothing Nothing Nothing)
else
SelectNoParens
<$> Gens.downscale Qc.arbitrary
<*> Qc.arbitrary
<*> Qc.arbitrary
<*> Qc.arbitrary
<*> Qc.arbitrary