Example

SQL Query

Small SQL-like queries.

Parse a small SQL-like query subset into clauses, expressions, tables, and conditions.

What it shows

  • Keyword covers query words, join types, select quantifiers, and sort directions.
  • Token covers literals, identifiers, column references, calls, aliases, and subqueries.
  • Condition composes comparisons and IN predicates with AND, OR, and groups.
  • Clause covers SELECT, FROM, JOIN, WHERE, GROUP BY, HAVING, ORDER BY, and LIMIT.
  • Query combines clauses into a complete statement.

Try changing a column to sum(orders.total).

Specs

spec.yml
$flags: [ 'CASE_INSENSITIVE', 'WORD_BOUNDARY', 'FLEXIBLE_WHITESPACE' ]

Keyword:
  Select: 'select'
  SelectQuantifier:
    Distinct: 'distinct'
    All: 'all'
  From: 'from'
  Join: 'join'
  JoinType:
    Inner: 'inner'
    LeftOuter: 'left( outer)?'
    RightOuter: 'right( outer)?'
    FullOuter: 'full( outer)?'
  On: 'on'
  Where: 'where'
  As: 'as'
  And: 'and'
  Or: 'or'
  In: 'in'
  GroupBy: 'group by'
  Having: 'having'
  OrderBy: 'order by'
  SortDirection:
    Ascending: 'asc'
    Descending: 'desc'
  Limit: 'limit'

ComparisonOperator:
  GreaterOrEqual: '>='
  LessOrEqual: '<='
  NotEqual: '<>|!='
  Equal: '='
  GreaterThan: '>'
  LessThan: '<'

Token:

  StringLiteral: [ '''.*?''', '".*?"' ]

  NumberLiteral: '\d+(\.\d+)?'

  Identifier: '${!Keyword}[A-Za-z_][A-Za-z0-9_]*'

  Wildcard: '\*'

  ColumnReference:
    $fields:
      table: Identifier
      column: Identifier
    $patterns: '#{table}\.#{column}'

  FunctionCall:
    $flags: OPTIONAL_WHITESPACE
    $fields:
      name: Identifier
      arguments: 'Token[]'
    $patterns: '#{name}\((#{arguments}(, #{arguments})*)?\)'

  AliasedExpression:
    $fields:
      value: Token
      alias: Identifier
    $patterns: '#{value} ${Keyword::As} #{alias}'

  Subquery:
    $fields:
      query: Query
    $patterns: '\(#{query}\)'

Condition:

  Comparison:
    $fields:
      left: Token
      operator: ComparisonOperator
      right: Token
    $patterns: '#{left} #{operator} #{right}'

  InPredicate:
    $flags: OPTIONAL_WHITESPACE
    $fields:
      expression: Token
      values: 'Token[]'
    $patterns: '#{expression} ${Keyword::In} \((#{values}(, #{values})*)\)'

  And:
    $fields:
      conditions: 'Condition[]'
    $patterns: '#{conditions}( ${Keyword::And} #{conditions})+'

  Or:
    $fields:
      conditions: 'Condition[]'
    $patterns: '#{conditions}( ${Keyword::Or} #{conditions})+'

  Group:
    $flags: OPTIONAL_WHITESPACE
    $fields:
      condition: Condition
    $patterns: '\( #{condition} \)'

Clause:

  Select:
    $flags: OPTIONAL_WHITESPACE
    $fields:
      quantifier: Keyword::SelectQuantifier
      items: 'Token[]'
    $patterns: '${Keyword::Select}( #{quantifier})? #{items}(, #{items})*'

  From:
    $fields:
      table: Token::Identifier
      alias: Token::Identifier
    $patterns: '${Keyword::From} #{table}( #{alias})?'

  Join:
    $fields:
      joinType: Keyword::JoinType
      table: Token::Identifier
      alias: Token::Identifier
      condition: Condition
    $patterns: '(#{joinType} )?${Keyword::Join} #{table}( #{alias})? ${Keyword::On} #{condition}'

  Where:
    $fields:
      condition: Condition
    $patterns: '${Keyword::Where} #{condition}'

  GroupBy:
    $flags: OPTIONAL_WHITESPACE
    $fields:
      expressions: 'Token[]'
    $patterns: '${Keyword::GroupBy} #{expressions}(, #{expressions})*'

  Having:
    $fields:
      condition: Condition
    $patterns: '${Keyword::Having} #{condition}'

  OrderBy:
    $flags: OPTIONAL_WHITESPACE
    $types:
      SortItem:
        $fields:
          expression: Token
          direction: Keyword::SortDirection
        $patterns: '#{expression}( #{direction})?'
    $fields:
      sortItems: 'SortItem[]'
    $patterns: '${Keyword::OrderBy} #{sortItems}(, #{sortItems})*'

  Limit:
    $fields:
      count: Token::NumberLiteral
    $patterns: '${Keyword::Limit} #{count}'

Query:
  $fields:
    select: Clause::Select
    from: Clause::From
    join: Clause::Join
    where: Clause::Where
    groupBy: Clause::GroupBy
    having: Clause::Having
    orderBy: Clause::OrderBy
    limit: Clause::Limit
  $patterns: '#{select} #{from}( #{join})?( #{where})?( #{groupBy})?( #{having})?( #{orderBy})?( #{limit})?'
Try it liveOpen SQL Query in StudioTweak the spec, paste your own input, and see the parse tree update in real time.

Or browse other examples →