Add support for intervals for AST, report filtering

This commit is contained in:
Denis Arh
2018-12-29 08:10:11 +01:00
parent c88f6cda6a
commit d1a07361bf
6 changed files with 74 additions and 3 deletions
+7
View File
@@ -40,6 +40,11 @@ type (
Keyword string
}
Interval struct {
Value string
Unit string
}
Column struct {
Expr ASTNodes
Alias string
@@ -58,6 +63,8 @@ func (n Operator) String() string { return n.Kind }
func (n Keyword) String() string { return n.Keyword }
func (n Interval) String() string { return fmt.Sprintf("INTERVAL %s %s", n.Value, n.Unit) }
func (n Function) String() string { return fmt.Sprintf("%s(%s)", n.Name, n.Arguments) }
func (n Ident) String() string { return n.Value }
+42 -1
View File
@@ -176,7 +176,11 @@ checkToken:
list = append(list, Operator{Kind: t.literal})
goto next
case KEYWORD:
list = append(list, Keyword{Keyword: t.literal})
if keyword, err := p.parseKeyword(t); err != nil {
return nil, err
} else {
list = append(list, keyword)
}
goto next
case NUMBER:
list = append(list, Number{Value: t.literal})
@@ -239,6 +243,13 @@ next:
case OPERATOR:
list = append(list, Operator{Kind: t.literal})
goto next
case KEYWORD:
if keyword, err := p.parseKeyword(t); err != nil {
return nil, err
} else {
list = append(list, keyword)
}
goto next
case IDENT:
if p.peekToken(1).Is(OPERATOR) {
// Looks like we have an expression ahead of us
@@ -280,3 +291,33 @@ next:
return nil, fmt.Errorf("unexpected token while parsing set (%v)", t)
}
}
func (p *Parser) parseKeyword(t Token) (list ASTNode, err error) {
switch strings.ToUpper(t.literal) {
case "INTERVAL":
i := Interval{Value: p.nextToken().literal}
u := p.nextToken()
if u.code != IDENT {
return nil, fmt.Errorf("expecting identifier, got %v", t)
} else {
switch strings.ToUpper(u.literal) {
case "MICROSECOND", "SECOND", "MINUTE", "HOUR",
"DAY", "WEEK", "MONTH", "QUARTER", "YEAR",
"SECOND_MICROSECOND", "MINUTE_MICROSECOND", "MINUTE_SECOND", "HOUR_MICROSECOND", "HOUR_SECOND",
"HOUR_MINUTE", "DAY_MICROSECOND", "DAY_SECOND", "DAY_MINUTE", "DAY_HOUR", "YEAR_MONTH":
// All good
break
default:
return nil, fmt.Errorf("expecting interval unit, got %v", u.literal)
}
}
i.Unit = u.literal
return i, nil
default:
return Keyword{Keyword: t.literal}, nil
}
}
+9
View File
@@ -129,6 +129,15 @@ func TestAstParser_Parser(t *testing.T) {
Number{Value: "1"},
},
},
{
parser: NewParser().ParseExpression,
in: `NOW() > DATE_SUB(col, INTERVAL 31 DAY)`,
tree: ASTNodes{
Function{Name: "NOW"},
Operator{Kind: ">"},
Function{Name: "DATE_SUB", Arguments: ASTSet{Ident{Value: "col"}, Interval{Value: "31", Unit: "DAY"}}},
},
},
{
parser: NewParser().ParseExpression,
in: `foo LIKE 'bar%'`,
+4
View File
@@ -75,6 +75,10 @@ func (n Keyword) ToSql() (string, []interface{}, error) {
return n.Keyword, nil, nil
}
func (n Interval) ToSql() (string, []interface{}, error) {
return fmt.Sprintf("INTERVAL ? %s", n.Unit), []interface{}{n.Value}, nil
}
func (n Operator) ToSql() (string, []interface{}, error) {
var op = n.Kind
+1 -1
View File
@@ -87,7 +87,7 @@ func (TokenConsumerIdent) Consume(s RuneReader) Token {
switch lit {
case "LIKE", "NOT", "AND", "OR", "XOR":
return Token{code: OPERATOR, literal: lit}
case "DESC", "ASC":
case "DESC", "ASC", "INTERVAL":
return Token{code: KEYWORD, literal: lit}
}
+11 -1
View File
@@ -52,7 +52,17 @@ func stdGroupByFuncHandler(f ql.Function) (ql.Function, error) {
default:
return f, fmt.Errorf("unsupported group-by function %q", f.Name)
}
}
// Identifiers should be names of the fields (physical table columns OR json fields, defined in module)
func stdFilterFuncHandler(f ql.Function) (ql.Function, error) {
switch strings.ToUpper(f.Name) {
case "CONCAT", "QUARTER", "YEAR", "DATE", "NOW", "DATE_ADD", "DATE_SUB", "DATE_FORMAT":
return f, nil
default:
return f, fmt.Errorf("unsupported group-by function %q", f.Name)
}
}
func NewRecordReportBuilder(moduleID uint64) *recordReportBuilder {
@@ -83,7 +93,7 @@ func (b *recordReportBuilder) SetFilter(filters string) (err error) {
p := ql.NewParser()
p.OnIdent = ql.MakeIdentWrapHandler(jsonWrap, "created_at", "updated_at", "id", "user_id")
p.OnFunction = stdGroupByFuncHandler
p.OnFunction = stdFilterFuncHandler
b.filter, err = p.ParseExpression(filters)
return