postgresql-syntax-0.5.0.2: library-internal/PostgresqlSyntax/Ast/TableRef.hs
module PostgresqlSyntax.Ast.TableRef
( TableRef (..),
)
where
import qualified HeadedMegaparsec as Parser
import PostgresqlSyntax.Algebra
import PostgresqlSyntax.Ast.AliasClause
import PostgresqlSyntax.Ast.FuncAliasClause
import PostgresqlSyntax.Ast.FuncTable
import PostgresqlSyntax.Ast.JoinedTable
import PostgresqlSyntax.Ast.RelationExpr
import {-# SOURCE #-} PostgresqlSyntax.Ast.SelectWithParens (SelectWithParens)
import PostgresqlSyntax.Ast.TablesampleClause
import qualified PostgresqlSyntax.Helpers.Gens as Gens
import qualified PostgresqlSyntax.Helpers.Parsers as Parsers
import qualified PostgresqlSyntax.Helpers.TextBuilders as TextBuilders
import PostgresqlSyntax.Prelude hiding (filter, head, many, some, tail, try)
import qualified Test.QuickCheck as Qc
-- |
-- ==== References
-- @
-- | relation_expr opt_alias_clause
-- | relation_expr opt_alias_clause tablesample_clause
-- | func_table func_alias_clause
-- | LATERAL_P func_table func_alias_clause
-- | xmltable opt_alias_clause
-- | LATERAL_P xmltable opt_alias_clause
-- | select_with_parens opt_alias_clause
-- | LATERAL_P select_with_parens opt_alias_clause
-- | joined_table
-- | '(' joined_table ')' alias_clause
--
-- TODO: Add xmltable
-- @
data TableRef
= -- |
-- @
-- | relation_expr opt_alias_clause
-- | relation_expr opt_alias_clause tablesample_clause
-- @
RelationExprTableRef RelationExpr (Maybe AliasClause) (Maybe TablesampleClause)
| -- |
-- @
-- | func_table func_alias_clause
-- | LATERAL_P func_table func_alias_clause
-- @
FuncTableRef Bool FuncTable (Maybe FuncAliasClause)
| -- |
-- @
-- | select_with_parens opt_alias_clause
-- | LATERAL_P select_with_parens opt_alias_clause
-- @
SelectTableRef Bool SelectWithParens (Maybe AliasClause)
| -- |
-- @
-- | joined_table
-- | '(' joined_table ')' alias_clause
-- @
JoinTableRef JoinedTable (Maybe AliasClause)
deriving (Show, Generic, Eq, Ord, Data)
instance IsAst TableRef where
toTextBuilder settings = \case
RelationExprTableRef a b c ->
TextBuilders.optLexemes
[ Just (toTextBuilder settings a),
fmap (toTextBuilder settings) b,
fmap (toTextBuilder settings) c
]
FuncTableRef a b c ->
TextBuilders.optLexemes
[ if a then Just "LATERAL" else Nothing,
Just (toTextBuilder settings b),
fmap (toTextBuilder settings) c
]
SelectTableRef a b c ->
TextBuilders.optLexemes
[ if a then Just "LATERAL" else Nothing,
Just (toTextBuilder settings b),
fmap (toTextBuilder settings) c
]
JoinTableRef a b -> case b of
Just c -> TextBuilders.renderInParens (toTextBuilder settings a) <> " " <> toTextBuilder settings c
Nothing -> toTextBuilder settings a
parser settings = Parser.label "table reference" (parseMaybeExtended @JoinedTable settings)
-- |
-- 'PostgresqlSyntax.Ast.JoinedTable' embeds trivially into a bare,
-- alias-less 'TableRef' (@joined_table@ is one of @table_ref@'s
-- alternatives), and a 'TableRef' of that exact shape is recognizable back
-- as one. See 'PostgresqlSyntax.Algebra.Extends' for how this is used to
-- fold a chain of joins onto a leading 'TableRef'.
instance Refines JoinedTable TableRef where
embed a = JoinTableRef a Nothing
project = \case
JoinTableRef a Nothing -> Just a
_ -> Nothing
-- |
-- Every @table_ref@ production except the left-recursive ones (those are
-- the @joined_table@ continuations, hosted by
-- "PostgresqlSyntax.Ast.JoinedTable"\'s
-- 'PostgresqlSyntax.Algebra.Extends' instance).
--
-- The two @joined_table@-shaped alternatives here are /not/ left-recursive:
-- both begin with a parenthesis, so neither can loop back into this parser
-- without consuming input. They reach @'(' joined_table ')'@ through
-- 'JoinedTable'\'s own 'parseBase' rather than through a
-- 'JoinedTable' helper export.
instance LeftRecursive TableRef where
parseBase settings =
asum
[ lateralTableRef,
Parser.wrapToHead nonLateralTableRef,
relationExprTableRef,
joinedTableWithAliasTableRef,
inParensJoinedTableTableRef
]
where
relationExprTableRef = do
relationExpr <- parser settings
Parser.endHead
optAliasClause <- optional (Parsers.space1 *> parser settings)
optTablesampleClause <- optional (Parsers.space1 *> parser settings)
return (RelationExprTableRef relationExpr optAliasClause optTablesampleClause)
lateralTableRef = do
Parsers.keyword "lateral"
Parsers.space1
Parser.endHead
lateralableTableRef True
nonLateralTableRef = lateralableTableRef False
lateralableTableRef lateral =
asum
[ do
a <- parser settings
b <- optional (Parsers.space1 *> parser settings)
return (FuncTableRef lateral a b),
do
select <- parser settings
optAliasClause <- optional $ Parsers.space1 *> parser settings
return (SelectTableRef lateral select optAliasClause)
]
inParensJoinedTableTableRef = JoinTableRef <$> parseBase @JoinedTable settings <*> pure Nothing
joinedTableWithAliasTableRef = do
jt <- Parser.wrapToHead (Parsers.inParens (parser settings))
Parsers.space1
alias <- parser settings
return (JoinTableRef jt (Just alias))
instance Qc.Arbitrary TableRef where
shrink = Qc.genericShrink
arbitrary =
Qc.sized $ \n ->
if n <= 1
then RelationExprTableRef <$> Qc.arbitrary <*> pure Nothing <*> pure Nothing
else
Qc.oneof
[ RelationExprTableRef <$> Qc.arbitrary <*> Gens.terminatingMaybe Qc.arbitrary <*> Gens.terminatingMaybe Qc.arbitrary,
FuncTableRef <$> Qc.arbitrary <*> Qc.arbitrary <*> Gens.terminatingMaybe Qc.arbitrary,
SelectTableRef <$> Qc.arbitrary <*> Gens.downscale Qc.arbitrary <*> Gens.terminatingMaybe Qc.arbitrary,
JoinTableRef <$> Qc.arbitrary <*> Gens.terminatingMaybe Qc.arbitrary
]