Modulepostgresql-syntax-0.4.1.1Haskell2010
PostgresqlSyntax.Parsing
Our parsing strategy is to port the original Postgres parser as closely as possible.
We're using the gram.y Postgres source file, which is the closest thing we have
to a Postgres syntax spec. Here's a link to it:
https://github.com/postgres/postgres/blob/master/src/backend/parser/gram.y.
Here's the essence of how the original parser is implemented, citing from PostgreSQL Wiki:
scan.l defines the lexer, i.e. the algorithm that splits a string (containing an SQL statement) into a stream of tokens. A token is usually a single word (i.e., doesn't contain spaces but is delimited by spaces), but can also be a whole single or double-quoted string for example. The lexer is basically defined in terms of regular expressions which describe the different token types.
gram.y defines the grammar (the syntactical structure) of SQL statements, using the tokens generated by the lexer as basic building blocks. The grammar is defined in BNF notation. BNF resembles regular expressions but works on the level of tokens, not characters. Also, patterns (called rules or productions in BNF) are named, and may be recursive, i.e. use themselves as sub-patterns.
- 1 type
- 211 values
- Packagepostgresql-syntax-0.4.1.1
- Exports212
- LanguageHaskell2010
- LicenceMIT
- SourceParsing.hs
testParser parser = either putStr print . run parserExecutors
1 declarationHelpers
11 declarationstestParser (quotedString '\'') "'abc''d'""abc'd"
PreparableStmt
1 declarationCall
1 declarationInsert
10 declarationsoverrideKind :: (Token s ~ Char, Tokens s ~ Text, Ord e, Stream s) => HeadedParsec e s OverrideKindUpdate
5 declarationsDelete
2 declarationsSelect
19 declarationstest = testParser selectStmttest "select id from as"... |1 | select id from as | ^Reserved keyword "as" used as an identifier. If that's what you intend, you have to wrap it in double quotes.
The one that doesn't start with "WITH".
References
| 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
selectBinOp :: (Token s ~ Char, Tokens s ~ Text, Ord e, Stream s) => HeadedParsec e s SelectBinOptestParser targetEl "a.b as c"AliasedExprTargetEl (CExprAExpr (ColumnrefCExpr (Columnref (UnquotedIdent "a") (Just (AttrNameIndirectionEl (UnquotedIdent "b") :| []))))) (UnquotedIdent "c")
Into clause details
1 declarationReferences
OptTempTableName:
| TEMPORARY opt_table qualified_name
| TEMP opt_table qualified_name
| LOCAL TEMPORARY opt_table qualified_name
| LOCAL TEMP opt_table qualified_name
| GLOBAL TEMPORARY opt_table qualified_name
| GLOBAL TEMP opt_table qualified_name
| UNLOGGED opt_table qualified_name
| TABLE qualified_name
| qualified_name
Group by details
1 declarationWindow clause details
8 declarationsReferences
window_specification:
| '(' opt_existing_window_name opt_partition_clause
opt_sort_clause opt_frame_clause ')'
References
opt_frame_clause:
| RANGE frame_extent opt_window_exclusion_clause
| ROWS frame_extent opt_window_exclusion_clause
| GROUPS frame_extent opt_window_exclusion_clause
| EMPTY
frameClauseMode :: (Token s ~ Char, Tokens s ~ Text, Ord e, Stream s) => HeadedParsec e s FrameClauseModeReferences
| UNBOUNDED PRECEDING
| UNBOUNDED FOLLOWING
| CURRENT_P ROW
| a_expr PRECEDING
| a_expr FOLLOWING
windowExclusionClause :: (Token s ~ Char, Tokens s ~ Text, Ord e, Stream s) => HeadedParsec e s WindowExclusionClauseTable refs
24 declarationstestParser tableRef "a left join b on (a.i = b.i)"JoinTableRef (MethJoinedTable (QualJoinMeth...
References
| '(' joined_table ')'
References
| table_ref CROSS JOIN table_ref
| table_ref join_type JOIN table_ref join_qual
| table_ref JOIN table_ref join_qual
| table_ref NATURAL join_type JOIN table_ref
| table_ref NATURAL JOIN table_ref
Where
2 declarationsSorting
2 declarationsExpressions
48 declarationsNotice that the tree constructed by this parser does not reflect the precedence order of Postgres. For the purposes of this library it simply doesn't matter, so we're not bothering with that.
Composite on the right:
testParser aExpr "a = b :: int4"SymbolicBinOpAExpr (CExprAExpr (ColumnrefCExpr (Columnref (UnquotedIdent "a") Nothing))) (MathSymbolicExprBinOp EqualsMathOp) (TypecastAExpr (CExprAExpr (ColumnrefCExpr (Columnref (UnquotedIdent "b") Nothing))) (Typename False (GenericTypeSimpleTypename (GenericType (UnquotedIdent "int4") Nothing Nothing)) False Nothing))
Composite on the left:
testParser aExpr "a = b :: int4 and c"SymbolicBinOpAExpr (CExprAExpr (ColumnrefCExpr (Columnref (UnquotedIdent "a") Nothing))) (MathSymbolicExprBinOp EqualsMathOp) (AndAExpr (TypecastAExpr (CExprAExpr (ColumnrefCExpr (Columnref (UnquotedIdent "b") Nothing))) (Typename False (GenericTypeSimpleTypename (GenericType (UnquotedIdent "int4") Nothing Nothing)) False Nothing)) (CExprAExpr (ColumnrefCExpr (Columnref (UnquotedIdent "c") Nothing))))
trimModifier :: (Token s ~ Char, Tokens s ~ Text, Ord e, Stream s) => HeadedParsec e s TrimModifierstarFuncApplicationParams :: (Token strm ~ Char, Stream strm, Ord err) => HeadedParsec err strm FuncApplicationParamsReferences
func_arg_expr:
| a_expr
| param_name COLON_EQUALS a_expr
| param_name EQUALS_GREATER a_expr
param_name:
| type_function_name
Ops
8 declarationsConstants
15 declarationstestParser aexprConst "32948023849023"IAexprConst 32948023849023
testParser aexprConst "'abc''de'"SAexprConst "abc'de"
testParser aexprConst "23.43234"FAexprConst 23.43234
testParser aexprConst "32423423.324324872"FAexprConst 3.2423423324324872e7
testParser aexprConst "NULL"NullAexprConst
References
AexprConst: Iconst
| FCONST
| Sconst
| BCONST
| XCONST
| func_name Sconst
| func_name '(' func_arg_list opt_sort_clause ')' Sconst
| ConstTypename Sconst
| ConstInterval Sconst opt_interval
| ConstInterval '(' Iconst ')' Sconst
| TRUE_P
| FALSE_P
| NULL_P
References
ConstDatetime:
| TIMESTAMP '(' Iconst ')' opt_timezone
| TIMESTAMP opt_timezone
| TIME '(' Iconst ')' opt_timezone
| TIME opt_timezone
Clauses
9 declarationsReferences
select_limit:
| limit_clause offset_clause
| offset_clause limit_clause
| limit_clause
| offset_clause
References
limit_clause:
| LIMIT select_limit_value
| LIMIT select_limit_value ',' select_offset_value
| FETCH first_or_next select_fetch_first_value row_or_rows ONLY
| FETCH first_or_next row_or_rows ONLY
References
select_limit_value:
| a_expr
| ALL
For Locking
4 declarationsReferences
for_locking_clause:
| for_locking_items
| FOR READ ONLY
for_locking_items:
| for_locking_item
| for_locking_items for_locking_item
References
for_locking_item:
| for_locking_strength locked_rels_list opt_nowait_or_skip
locked_rels_list:
| OF qualified_name_list
| EMPTY
opt_nowait_or_skip:
| NOWAIT
| SKIP LOCKED
| EMPTY
forLockingStrength :: (Token s ~ Char, Tokens s ~ Text, Ord e, Stream s) => HeadedParsec e s ForLockingStrengthReferences
for_locking_strength:
| FOR UPDATE
| FOR NO KEY UPDATE
| FOR SHARE
| FOR KEY SHARE
References & Names
25 declarationsReferences
ident_start [A-Za-z200-377_]
ident_cont [A-Za-z200-377_0-9$]
identifier {ident_start}{ident_cont}*
References
ColId:
| IDENT
| unreserved_keyword
| col_name_keyword
References
ColLabel:
| IDENT
| unreserved_keyword
| col_name_keyword
| type_func_name_keyword
| reserved_keyword
testParser qualifiedName "a.b"IndirectedQualifiedName (UnquotedIdent "a") (AttrNameIndirectionEl (UnquotedIdent "b") :| [])
testParser qualifiedName "a.-"...expecting '*', column label, or white space
References
qualified_name:
| ColId
| ColId indirection
References
func_name:
| type_function_name
| ColId indirection
References
type_function_name:
| IDENT
| unreserved_keyword
| type_func_name_keyword
References
indirection:
| indirection_el
| indirection indirection_el
References
attr_name:
| ColLabel
Expected keyword
Consume a keyphrase, ignoring case and types of spaces between words.
Typename
7 declarationsIndexes
7 declarationsnullsOrder :: (Token s ~ Char, Tokens s ~ Text, Ord e, Stream s) => HeadedParsec e s NullsOrder