private void parseGroupBy(OracleSelectQueryBlock queryBlock) {
    if (lexer.token() == (Token.GROUP)) {
      lexer.nextToken();
      accept(Token.BY);

      SQLSelectGroupByClause groupBy = new SQLSelectGroupByClause();
      for (; ; ) {
        if (identifierEquals("GROUPING")) {
          GroupingSetExpr groupingSet = new GroupingSetExpr();
          lexer.nextToken();
          acceptIdentifier("SETS");
          accept(Token.LPAREN);
          createExprParser().exprList(groupingSet.getParameters());
          accept(Token.RPAREN);
          groupBy.getItems().add(groupingSet);
        } else {
          groupBy.getItems().add(this.createExprParser().expr());
        }

        if (!(lexer.token() == (Token.COMMA))) {
          break;
        }

        lexer.nextToken();
      }

      if (lexer.token() == (Token.HAVING)) {
        lexer.nextToken();

        groupBy.setHaving(this.createExprParser().expr());
      }

      queryBlock.setGroupBy(groupBy);
    }
  }
  protected SQLSelectQuery query() {
    if (lexer.token() == (Token.LPAREN)) {
      lexer.nextToken();

      SQLSelectQuery select = query();
      accept(Token.RPAREN);

      return queryRest(select);
    }

    accept(Token.SELECT);

    OracleSelectQueryBlock queryBlock = new OracleSelectQueryBlock();
    parseHints(queryBlock);

    if (lexer.token() == (Token.DISTINCT)) {
      queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT);
      lexer.nextToken();
    } else if (lexer.token() == (Token.UNIQUE)) {
      queryBlock.setDistionOption(SQLSetQuantifier.UNIQUE);
      lexer.nextToken();
    } else if (lexer.token() == (Token.ALL)) {
      queryBlock.setDistionOption(SQLSetQuantifier.ALL);
      lexer.nextToken();
    }

    parseSelectList(queryBlock);

    parseFrom(queryBlock);

    parseWhere(queryBlock);

    parseHierachical(queryBlock);

    parseGroupBy(queryBlock);

    parseModelClause(queryBlock);

    return queryRest(queryBlock);
  }
  private void parseHierachical(OracleSelectQueryBlock queryBlock) {
    OracleSelectHierachicalQueryClause hierachical = null;

    if (lexer.token() == Token.CONNECT) {
      hierachical = new OracleSelectHierachicalQueryClause();
      lexer.nextToken();
      accept(Token.BY);

      if (lexer.token() == Token.PRIOR) {
        lexer.nextToken();
        hierachical.setPrior(true);
      }

      if (identifierEquals("NOCYCLE")) {
        hierachical.setNoCycle(true);
      }
      hierachical.setConnectBy(this.createExprParser().expr());
    }

    if (lexer.token() == Token.START) {
      lexer.nextToken();
      if (hierachical == null) {
        hierachical = new OracleSelectHierachicalQueryClause();
      }
      accept(Token.WITH);

      hierachical.setStartWith(this.createExprParser().expr());
    }

    if (lexer.token() == Token.CONNECT) {
      if (hierachical == null) {
        hierachical = new OracleSelectHierachicalQueryClause();
      }

      lexer.nextToken();
      accept(Token.BY);

      if (lexer.token() == Token.PRIOR) {
        lexer.nextToken();
        hierachical.setPrior(true);
      }

      if (identifierEquals("NOCYCLE")) {
        hierachical.setNoCycle(true);
      }
      hierachical.setConnectBy(this.createExprParser().expr());
    }

    if (hierachical != null) {
      queryBlock.setHierachicalQueryClause(hierachical);
    }
  }
  private void parseModelClause(OracleSelectQueryBlock queryBlock) {
    if (lexer.token() != Token.MODEL) {
      return;
    }

    lexer.nextToken();

    ModelClause model = new ModelClause();
    parseCellReferenceOptions(model.getCellReferenceOptions());

    if (identifierEquals("RETURN")) {
      lexer.nextToken();
      ReturnRowsClause returnRowsClause = new ReturnRowsClause();
      if (lexer.token() == Token.ALL) {
        lexer.nextToken();
        returnRowsClause.setAll(true);
      } else {
        acceptIdentifier("UPDATED");
      }
      acceptIdentifier("ROWS");

      model.setReturnRowsClause(returnRowsClause);
    }

    while (lexer.token() == Token.REFERENCE) {
      ReferenceModelClause referenceModelClause = new ReferenceModelClause();
      lexer.nextToken();

      SQLExpr name = expr();
      referenceModelClause.setName(name);

      accept(Token.ON);
      accept(Token.LPAREN);
      OracleSelect subQuery = this.select();
      accept(Token.RPAREN);
      referenceModelClause.setSubQuery(subQuery);

      parseModelColumnClause(referenceModelClause);

      parseCellReferenceOptions(referenceModelClause.getCellReferenceOptions());

      model.getReferenceModelClauses().add(referenceModelClause);
    }

    parseMainModelClause(model);

    queryBlock.setModelClause(model);
  }
 private void parseHints(OracleSelectQueryBlock queryBlock) {
   if (lexer.token() == Token.HINT) {
     queryBlock.getHints().add(new OracleHint(lexer.stringVal()));
     lexer.nextToken();
   }
 }