mirror of
https://github.com/chaisql/chai.git
synced 2025-10-30 18:36:27 +08:00
157 lines
3.2 KiB
Go
157 lines
3.2 KiB
Go
package parser
|
|
|
|
import (
|
|
"github.com/genjidb/genji/internal/expr"
|
|
"github.com/genjidb/genji/internal/query"
|
|
"github.com/genjidb/genji/internal/sql/scanner"
|
|
)
|
|
|
|
// parseSelectStatement parses a select string and returns a Statement AST object.
|
|
// This function assumes the SELECT token has already been consumed.
|
|
func (p *Parser) parseSelectStatement() (*query.SelectStmt, error) {
|
|
var stmt query.SelectStmt
|
|
var err error
|
|
|
|
stmt.Distinct, err = p.parseDistinct()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse path list or query.Wildcard
|
|
stmt.ProjectionExprs, err = p.parseProjectedExprs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse "FROM".
|
|
var found bool
|
|
stmt.TableName, found, err = p.parseFrom()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !found {
|
|
return &stmt, nil
|
|
}
|
|
|
|
// Parse condition: "WHERE expr".
|
|
stmt.WhereExpr, err = p.parseCondition()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse group by: "GROUP BY expr"
|
|
stmt.GroupByExpr, err = p.parseGroupBy()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse order by: "ORDER BY path [ASC|DESC]?"
|
|
stmt.OrderBy, stmt.OrderByDirection, err = p.parseOrderBy()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse limit: "LIMIT expr"
|
|
stmt.LimitExpr, err = p.parseLimit()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse offset: "OFFSET expr"
|
|
stmt.OffsetExpr, err = p.parseOffset()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &stmt, nil
|
|
}
|
|
|
|
// parseProjectedExprs parses the list of projected fields.
|
|
func (p *Parser) parseProjectedExprs() ([]expr.Expr, error) {
|
|
// Parse first (required) result path.
|
|
pe, err := p.parseProjectedExpr()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pexprs := []expr.Expr{pe}
|
|
|
|
// Parse remaining (optional) result fields.
|
|
for {
|
|
if tok, _, _ := p.ScanIgnoreWhitespace(); tok != scanner.COMMA {
|
|
p.Unscan()
|
|
return pexprs, nil
|
|
}
|
|
|
|
if pe, err = p.parseProjectedExpr(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pexprs = append(pexprs, pe)
|
|
}
|
|
}
|
|
|
|
// parseProjectedExpr parses one projected expression.
|
|
func (p *Parser) parseProjectedExpr() (expr.Expr, error) {
|
|
// Check if the * token exists.
|
|
if tok, _, _ := p.ScanIgnoreWhitespace(); tok == scanner.MUL {
|
|
return expr.Wildcard{}, nil
|
|
}
|
|
p.Unscan()
|
|
|
|
e, err := p.ParseExpr()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rf := &expr.NamedExpr{Expr: e, ExprName: e.String()}
|
|
|
|
// Check if the AS token exists.
|
|
if tok, _, _ := p.ScanIgnoreWhitespace(); tok == scanner.AS {
|
|
rf.ExprName, err = p.parseIdent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rf, nil
|
|
}
|
|
p.Unscan()
|
|
|
|
return rf, nil
|
|
}
|
|
|
|
func (p *Parser) parseDistinct() (bool, error) {
|
|
if tok, _, _ := p.ScanIgnoreWhitespace(); tok != scanner.DISTINCT {
|
|
p.Unscan()
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (p *Parser) parseFrom() (string, bool, error) {
|
|
if ok, err := p.parseOptional(scanner.FROM); !ok || err != nil {
|
|
return "", false, err
|
|
}
|
|
|
|
// Parse table name
|
|
ident, err := p.parseIdent()
|
|
if err != nil {
|
|
pErr := err.(*ParseError)
|
|
pErr.Expected = []string{"table_name"}
|
|
return ident, true, pErr
|
|
}
|
|
|
|
return ident, true, nil
|
|
}
|
|
|
|
func (p *Parser) parseGroupBy() (expr.Expr, error) {
|
|
ok, err := p.parseOptional(scanner.GROUP, scanner.BY)
|
|
if err != nil || !ok {
|
|
return nil, err
|
|
}
|
|
|
|
// parse expr
|
|
e, err := p.ParseExpr()
|
|
return e, err
|
|
}
|