Skip to content

Commit

Permalink
fix #1799: change regex delim to '/' & RHS only
Browse files Browse the repository at this point in the history
  • Loading branch information
dgnorton committed Mar 3, 2015
1 parent 08796bd commit 9d6d36f
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 36 deletions.
12 changes: 6 additions & 6 deletions httpd/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1913,7 +1913,7 @@ func TestHandler_serveShowSeries(t *testing.T) {

// SHOW SERIES WHERE =~ regex
{
q: "SHOW SERIES WHERE region =~ `ca.*`",
q: `SHOW SERIES WHERE region =~ /ca.*/`,
r: &influxdb.Results{
Results: []*influxdb.Result{
{
Expand All @@ -1933,7 +1933,7 @@ func TestHandler_serveShowSeries(t *testing.T) {

// SHOW SERIES WHERE !~ regex
{
q: "SHOW SERIES WHERE host !~ `server0[12]`",
q: `SHOW SERIES WHERE host !~ /server0[12]/`,
r: &influxdb.Results{
Results: []*influxdb.Result{
{
Expand Down Expand Up @@ -2046,13 +2046,13 @@ func TestHandler_serveShowMeasurements(t *testing.T) {

// SHOW MEASUREMENTS WHERE =~ regex
{
q: "SHOW MEASUREMENTS WHERE region =~ `ca.*`",
q: `SHOW MEASUREMENTS WHERE region =~ /ca.*/`,
r: `{"results":[{"series":[{"name":"measurements","columns":["name"],"values":[["gpu"],["other"]]}]}]}`,
},

// SHOW MEASUREMENTS WHERE !~ regex
{
q: "SHOW MEASUREMENTS WHERE region !~ `ca.*`",
q: `SHOW MEASUREMENTS WHERE region !~ /ca.*/`,
r: `{"results":[{"series":[{"name":"measurements","columns":["name"],"values":[["cpu"]]}]}]}`,
},
}
Expand Down Expand Up @@ -2302,7 +2302,7 @@ func TestHandler_serveShowTagValues(t *testing.T) {
},
// SHOW TAG VALUES FROM ... WHERE =~ regex
{
q: "SHOW TAG VALUES WITH KEY = host WHERE region =~ `ca.*`",
q: `SHOW TAG VALUES WITH KEY = host WHERE region =~ /ca.*/`,
r: &influxdb.Results{
Results: []*influxdb.Result{
{
Expand All @@ -2321,7 +2321,7 @@ func TestHandler_serveShowTagValues(t *testing.T) {
},
// SHOW TAG VALUES FROM ... WHERE !~ regex
{
q: "SHOW TAG VALUES WITH KEY = region WHERE host !~ `server0[12]`",
q: `SHOW TAG VALUES WITH KEY = region WHERE host !~ /server0[12]/`,
r: &influxdb.Results{
Results: []*influxdb.Result{
{
Expand Down
40 changes: 36 additions & 4 deletions influxql/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1456,6 +1456,8 @@ func (p *Parser) parseSortField() (*SortField, error) {

// ParseExpr parses an expression.
func (p *Parser) ParseExpr() (Expr, error) {
var err error

// Parse a non-binary expression type to start.
// This variable will always be the root of the expression tree.
expr, err := p.parseUnaryExpr()
Expand All @@ -1472,10 +1474,18 @@ func (p *Parser) ParseExpr() (Expr, error) {
return expr, nil
}

// Otherwise parse the next unary expression.
rhs, err := p.parseUnaryExpr()
if err != nil {
return nil, err
// Otherwise parse the next expression.
var rhs Expr
if IsRegexOp(op) {
// RHS of a regex operator must be a regular expression.
p.consumeWhitespace()
if rhs, err = p.parseRegex(); err != nil {
return nil, err
}
} else {
if rhs, err = p.parseUnaryExpr(); err != nil {
return nil, err
}
}

// Assign the new root based on the precendence of the LHS and RHS operators.
Expand Down Expand Up @@ -1565,6 +1575,28 @@ func (p *Parser) parseUnaryExpr() (Expr, error) {
}
}

// parseRegex parses a regular expression.
func (p *Parser) parseRegex() (Expr, error) {
tok, pos, lit := p.s.s.ScanRegex()

if tok == BADESCAPE {
msg := fmt.Sprintf("bad escape: %s", lit)
return nil, &ParseError{Message: msg, Pos: pos}
} else if tok == BADREGEX {
msg := fmt.Sprintf("bad regex: %s", lit)
return nil, &ParseError{Message: msg, Pos: pos}
} else if tok != REGEX {
return nil, newParseError(tokstr(tok, lit), []string{"regex"}, pos)
}

re, err := regexp.Compile(lit)
if err != nil {
return nil, &ParseError{Message: err.Error(), Pos: pos}
}

return &RegexLiteral{Val: re}, nil
}

// parseCall parses a function call.
// This function assumes the function name and LPAREN have been consumed.
func (p *Parser) parseCall(name string) (*Call, error) {
Expand Down
18 changes: 4 additions & 14 deletions influxql/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ func TestParser_ParseStatement(t *testing.T) {
},
},

// SELECT * FROM cpu WHERE host = 'serverC' AND region =~ `.*west.*`
// SELECT * FROM cpu WHERE host = 'serverC' AND region =~ /.*west.*/
{
s: "SELECT * FROM cpu WHERE host = 'serverC' AND region =~ `.*west.*`",
s: `SELECT * FROM cpu WHERE host = 'serverC' AND region =~ /.*west.*/`,
stmt: &influxql.SelectStatement{
Fields: []*influxql.Field{{Expr: &influxql.Wildcard{}}},
Source: &influxql.Measurement{Name: "cpu"},
Expand Down Expand Up @@ -861,26 +861,16 @@ func TestParser_ParseExpr(t *testing.T) {
},
},

// Binary expression with regex on right.
// Binary expression with regex.
{
s: "region =~ `us.*`",
s: "region =~ /us.*/",
expr: &influxql.BinaryExpr{
Op: influxql.EQREGEX,
LHS: &influxql.VarRef{Val: "region"},
RHS: &influxql.RegexLiteral{Val: regexp.MustCompile(`us.*`)},
},
},

// Binary expression with NEQ regex on left.
{
s: "`us.*` !~ region",
expr: &influxql.BinaryExpr{
Op: influxql.NEQREGEX,
RHS: &influxql.VarRef{Val: "region"},
LHS: &influxql.RegexLiteral{Val: regexp.MustCompile(`us.*`)},
},
},

// Complex binary expression.
{
s: `value + 3 < 30 AND 1 + 2 OR true`,
Expand Down
23 changes: 15 additions & 8 deletions influxql/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ func (s *Scanner) Scan() (tok Token, pos Pos, lit string) {
return COMMA, pos, ""
case ';':
return SEMICOLON, pos, ""
case '`':
return s.scanRegex()
}

return ILLEGAL, pos, string(ch0)
Expand Down Expand Up @@ -181,14 +179,13 @@ func (s *Scanner) scanString() (tok Token, pos Pos, lit string) {
return STRING, pos, lit
}

func (s *Scanner) scanRegex() (tok Token, pos Pos, lit string) {
s.r.unread()
func (s *Scanner) ScanRegex() (tok Token, pos Pos, lit string) {
_, pos = s.r.curr()

// Start & end sentinels.
start, end := '`', '`'
start, end := '/', '/'
// Valid escape chars.
escapes := map[rune]rune{'`': '`'}
escapes := map[rune]rune{'/': '/'}

b, err := ScanDelimited(s.r, start, end, escapes)

Expand Down Expand Up @@ -322,6 +319,16 @@ func newBufScanner(r io.Reader) *bufScanner {

// Scan reads the next token from the scanner.
func (s *bufScanner) Scan() (tok Token, pos Pos, lit string) {
return s.scanFunc(s.s.Scan)
}

// ScanRegex reads a regex token from the scanner.
func (s *bufScanner) ScanRegex() (tok Token, pos Pos, lit string) {
return s.scanFunc(s.s.ScanRegex)
}

// scanFunc uses the provided function to scan the next token.
func (s *bufScanner) scanFunc(scan func() (Token, Pos, string)) (tok Token, pos Pos, lit string) {
// If we have unread tokens then read them off the buffer first.
if s.n > 0 {
s.n--
Expand All @@ -331,7 +338,7 @@ func (s *bufScanner) Scan() (tok Token, pos Pos, lit string) {
// Move buffer position forward and save the token.
s.i = (s.i + 1) % len(s.buf)
buf := &s.buf[s.i]
buf.tok, buf.pos, buf.lit = s.s.Scan()
buf.tok, buf.pos, buf.lit = scan()

return s.curr()
}
Expand Down Expand Up @@ -442,7 +449,7 @@ func ScanDelimited(r io.RuneScanner, start, end rune, escapes map[rune]rune) ([]
if ch, _, err := r.ReadRune(); err != nil {
return nil, err
} else if ch != start {
return nil, fmt.Errorf("expected %s; found %s", string(ch), string(start))
return nil, fmt.Errorf("expected %s; found %s", string(start), string(ch))
}

var buf bytes.Buffer
Expand Down
4 changes: 0 additions & 4 deletions influxql/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,6 @@ func TestScanner_Scan(t *testing.T) {
{s: `10w`, tok: influxql.DURATION_VAL, lit: `10w`},
{s: `10x`, tok: influxql.NUMBER, lit: `10`}, // non-duration unit

// Regular expressions
{s: "`.*`", tok: influxql.REGEX, lit: ".*"},
{s: "`.*\\``", tok: influxql.REGEX, lit: ".*`"},

// Keywords
{s: `ALL`, tok: influxql.ALL},
{s: `ALTER`, tok: influxql.ALTER},
Expand Down

0 comments on commit 9d6d36f

Please sign in to comment.