module Selekt grammar SQL rule sql_query space? select_statement (space order_by_clause)? (space limit_clause)? space? end rule select_statement select_clause (space from_clause)? (space where_clause)? (space group_by_clause)? (space having_clause)? (space named_window_clause)? (space set_operator space select_statement)* end rule select_clause SELECT space (DISTINCT space)? projection (comma projection)* end rule from_clause FROM space source_with_joins (comma source_with_joins)* / FROM space source_with_joins (comma source_with_joins)* end rule source_with_joins source (space source_join)* / '(' source (space source_join)* ')' end rule source ((table ) (alias )? / subquery (alias )) end rule source_join regular_join_type space source space join_clause end rule regular_join_type ((INNER / LEFT / RIGHT / FULL space OUTER) space)? JOIN end rule join_clause ON space boolean_expression / USING space? '(' space? identifier (comma identifier)* ')' end rule comma space? ',' space? end rule subquery '(' space? select_statement space? ')' end rule table schema_table / schemaless_table end rule schemaless_table identifier { def schema_name nil end def table_name elements[0].value end } end rule schema_table schema '.' identifier { def schema_name schema.value end def table_name elements.last.value end } end rule schema identifier end rule column table_column / identifier end rule table_column identifier '.' identifier end rule projection '*' / table '.' '*' / expression alias? end rule alias space (AS space)? identifier end rule where_clause WHERE space expression end rule group_by_clause GROUP space BY space expression (comma expression)* end rule having_clause HAVING space expression end rule order_by_clause ORDER space BY space order_expression (comma order_expression)* end rule limit_clause LIMIT space integer (space OFFSET space integer)? end rule order_expression expression (space (ASC / DESC) (space NULLS space (FIRST / LAST))?)? end rule expression boolean_expression end rule single_expression (interval_expression / case_expression / function_call / column / literal / subquery / '(' space? expression space? ')') (space? '::' space? type)? end rule case_expression CASE space (expression space)? (WHEN space expression space THEN space expression space)+ (ELSE space expression space)? END end rule interval_expression INTERVAL space expression end rule type unquoted_identifier end rule function_call ( COUNT '(' space? (DISTINCT space)? ('*' / expression) space? ')' / unquoted_identifier '(' (space? expression (comma expression)*)? space? ')' ) (space OVER (space? window_clause / space identifier))? end rule window_clause '(' space? (partition_by_clause space)? order_by_clause space? ')' end rule named_window_clause WINDOW space identifier space AS space? window_clause end rule partition_by_clause PARTITION space BY space expression (comma expression)* end rule arithmetic_expression single_expression (space? arithmetic_operator space? single_expression)* end rule arithmetic_operator '+' / '-' / '*' / '/' / '%' / '||' end rule comparison_expression EXISTS space? subquery / arithmetic_expression space IS (space NOT)? space boolean / arithmetic_expression space (NOT space)? IN space list_of_values / arithmetic_expression (space? comparison_operator space? arithmetic_expression)? end rule list_of_values subquery / '(' space? expression (comma expression)* space? ')' end rule comparison_operator '<=' / '>=' / '<>' / '>' / '<' / '!=' / '=' / LIKE / ILIKE end rule negatable_expression (NOT space)? comparison_expression end rule boolean_expression negatable_expression (space boolean_operator space negatable_expression)* end rule boolean_operator AND / OR end rule set_operator UNION (space ALL)? end rule space [ \t\r\n]+ (comment space?)? end rule comment '--' [^\r\n]* end rule identifier quoted_identifier / unquoted_identifier !{|seq| seq[0].reserved? } { def value elements.first.value end } end rule unquoted_identifier [A-Za-z] [_A-Za-z0-9]* { def value text_value end def reserved? Selekt::RESERVED_SQL_KEYWORDS.include?(text_value.downcase) end } end rule quoted_identifier '"' body:('""' / [^"])* '"' { def value body.text_value.gsub('""', '"') end } end rule literal string / float / integer / boolean end rule string "'" ("''" / [^'])* "'" end rule boolean TRUE / FALSE / NULL end rule integer "-"? [0-9]+ end rule float "-"? [0-9]* '.' [0-9]+ end rule SELECT [Ss] [Ee] [Ll] [Ee] [Cc] [Tt] end rule DISTINCT [Dd] [Ii] [Ss] [Tt] [Ii] [Nn] [Cc] [Tt] end rule FROM [Ff] [Rr] [Oo] [Mm] end rule HAVING [Hh] [Aa] [Vv] [Ii] [Nn] [Gg] end rule ORDER [Oo] [Rr] [Dd] [Ee] [Rr] end rule ASC [Aa] [Ss] [Cc] end rule DESC [Dd] [Ee] [Ss] [Cc] end rule NULLS [Nn] [Uu] [Ll] [Ll] [Ss] end rule FIRST [Ff] [Ii] [Rr] [Ss] [Tt] end rule LAST [Ll] [Aa] [Ss] [Tt] end rule AS [Aa] [Ss] end rule JOIN [Jj] [Oo] [Ii] [Nn] end rule FULL [Ff] [Uu] [Ll] [Ll] end rule OUTER [Oo] [Uu] [Tt] [Ee] [Rr] end rule INNER [Ii] [Nn] [Nn] [Ee] [Rr] end rule LEFT [Ll] [Ee] [Ff] [Tt] end rule RIGHT [Rr] [Ii] [Gg] [Hh] [Tt] end rule ON [Oo] [Nn] end rule USING [Uu] [Ss] [Ii] [Nn] [Gg] end rule WHERE [Ww] [Hh] [Ee] [Rr] [Ee] end rule GROUP [Gg] [Rr] [Oo] [Uu] [Pp] end rule BY [Bb] [Yy] end rule LIMIT [Ll] [Ii] [Mm] [Ii] [Tt] end rule OFFSET [Oo] [Ff] [Ff] [Ss] [Ee] [Tt] end rule UNION [Uu] [Nn] [Ii] [Oo] [Nn] end rule ALL [Aa] [Ll] [Ll] end rule COUNT [Cc] [Oo] [Uu] [Nn] [Tt] end rule OVER [Oo] [Vv] [Ee] [Rr] end rule PARTITION [Pp] [Aa] [Rr] [Tt] [Ii] [Tt] [Ii] [Oo] [Nn] end rule WINDOW [Ww] [Ii] [Nn] [Dd] [Oo] [Ww] end rule CASE [Cc] [Aa] [Ss] [Ee] end rule WHEN [Ww] [Hh] [Ee] [Nn] end rule THEN [Tt] [Hh] [Ee] [Nn] end rule ELSE [Ee] [Ll] [Ss] [Ee] end rule END [Ee] [Nn] [Dd] end rule INTERVAL [Ii] [Nn] [Tt] [Ee] [Rr] [Vv] [Aa] [Ll] end rule IS [Ii] [Ss] end rule IN [Ii] [Nn] end rule EXISTS [Ee] [Xx] [Ii] [Ss] [Tt] [Ss] end rule NOT [Nn] [Oo] [Tt] end rule AND [Aa] [Nn] [Dd] end rule OR [Oo] [Rr] end rule TRUE [Tt] [Rr] [Uu] [Ee] end rule FALSE [Ff] [Aa] [Ll] [Ss] [Ee] end rule NULL [Nn] [Uu] [Ll] [Ll] end rule LIKE [Ll] [Ii] [Kk] [Ee] end rule ILIKE [Ii] [Ll] [Ii] [Kk] [Ee] end end end