Merge pull request #1334 from influxdb/ident-scan

Ident scanning, single quote strings
pull/1337/head
Ben Johnson 2015-01-19 16:16:56 -07:00
commit 8a2b078a10
9 changed files with 137 additions and 149 deletions

View File

@ -367,7 +367,7 @@ func TestHandler_CreateUser(t *testing.T) {
s := NewHTTPServer(srvr)
defer s.Close()
query := map[string]string{"q": `CREATE USER testuser WITH PASSWORD "1337"`}
query := map[string]string{"q": `CREATE USER testuser WITH PASSWORD '1337'`}
status, body := MustHTTP("GET", s.URL+`/query`, query, nil, "")
if status != http.StatusOK {
t.Fatalf("unexpected status: %d", status)

View File

@ -641,9 +641,6 @@ func MatchSource(src Source, name string) string {
// Target represents a target (destination) policy, measurment, and DB.
type Target struct {
// Retention policy to write into.
RetentionPolicy string
// Measurement to write into.
Measurement string
@ -655,12 +652,6 @@ type Target struct {
func (t *Target) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("INTO ")
if t.RetentionPolicy != "" {
_, _ = buf.WriteString(t.RetentionPolicy)
_, _ = buf.WriteString(".")
}
_, _ = buf.WriteString(t.Measurement)
if t.Database != "" {
@ -1146,7 +1137,7 @@ type StringLiteral struct {
}
// String returns a string representation of the literal.
func (l *StringLiteral) String() string { return Quote(l.Val) }
func (l *StringLiteral) String() string { return QuoteString(l.Val) }
// TimeLiteral represents a point-in-time literal.
type TimeLiteral struct {

View File

@ -53,23 +53,23 @@ func TestSelectStatement_Substatement(t *testing.T) {
// 3. Join with condition
{
stmt: `SELECT sum(aa.value) + sum(bb.value) FROM join(aa, bb) WHERE aa.host = "servera" AND bb.host = "serverb"`,
stmt: `SELECT sum(aa.value) + sum(bb.value) FROM join(aa, bb) WHERE aa.host = 'servera' AND bb.host = 'serverb'`,
expr: &influxql.VarRef{Val: "bb.value"},
sub: `SELECT bb.value FROM bb WHERE bb.host = "serverb"`,
sub: `SELECT bb.value FROM bb WHERE bb.host = 'serverb'`,
},
// 4. Join with complex condition
{
stmt: `SELECT sum(aa.value) + sum(bb.value) FROM join(aa, bb) WHERE aa.host = "servera" AND (bb.host = "serverb" OR bb.host = "serverc") AND 1 = 2`,
stmt: `SELECT sum(aa.value) + sum(bb.value) FROM join(aa, bb) WHERE aa.host = 'servera' AND (bb.host = 'serverb' OR bb.host = 'serverc') AND 1 = 2`,
expr: &influxql.VarRef{Val: "bb.value"},
sub: `SELECT bb.value FROM bb WHERE (bb.host = "serverb" OR bb.host = "serverc") AND 1.000 = 2.000`,
sub: `SELECT bb.value FROM bb WHERE (bb.host = 'serverb' OR bb.host = 'serverc') AND 1.000 = 2.000`,
},
// 5. 4 with different condition order
{
stmt: `SELECT sum(aa.value) + sum(bb.value) FROM join(aa, bb) WHERE ((bb.host = "serverb" OR bb.host = "serverc") AND aa.host = "servera") AND 1 = 2`,
stmt: `SELECT sum(aa.value) + sum(bb.value) FROM join(aa, bb) WHERE ((bb.host = 'serverb' OR bb.host = 'serverc') AND aa.host = 'servera') AND 1 = 2`,
expr: &influxql.VarRef{Val: "bb.value"},
sub: `SELECT bb.value FROM bb WHERE ((bb.host = "serverb" OR bb.host = "serverc")) AND 1.000 = 2.000`,
sub: `SELECT bb.value FROM bb WHERE ((bb.host = 'serverb' OR bb.host = 'serverc')) AND 1.000 = 2.000`,
},
}
@ -146,7 +146,7 @@ func TestFold(t *testing.T) {
{`60m + 50`, `1h + 50.000`},
// String literals.
{`"foo" + 'bar'`, `"foobar"`},
{`'foo' + 'bar'`, `'foobar'`},
} {
// Fold expression.
now := mustParseTime("2000-01-01T00:00:00Z")
@ -175,25 +175,25 @@ func TestTimeRange(t *testing.T) {
min, max string
}{
// LHS VarRef
{expr: `time > "2000-01-01 00:00:00"`, min: `2000-01-01 00:00:00.000001`, max: `0001-01-01 00:00:00`},
{expr: `time >= "2000-01-01 00:00:00"`, min: `2000-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
{expr: `time < "2000-01-01 00:00:00"`, min: `0001-01-01 00:00:00`, max: `1999-12-31 23:59:59.999999`},
{expr: `time <= "2000-01-01 00:00:00"`, min: `0001-01-01 00:00:00`, max: `2000-01-01 00:00:00`},
{expr: `time > '2000-01-01 00:00:00'`, min: `2000-01-01 00:00:00.000001`, max: `0001-01-01 00:00:00`},
{expr: `time >= '2000-01-01 00:00:00'`, min: `2000-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
{expr: `time < '2000-01-01 00:00:00'`, min: `0001-01-01 00:00:00`, max: `1999-12-31 23:59:59.999999`},
{expr: `time <= '2000-01-01 00:00:00'`, min: `0001-01-01 00:00:00`, max: `2000-01-01 00:00:00`},
// RHS VarRef
{expr: `"2000-01-01 00:00:00" > time`, min: `0001-01-01 00:00:00`, max: `1999-12-31 23:59:59.999999`},
{expr: `"2000-01-01 00:00:00" >= time`, min: `0001-01-01 00:00:00`, max: `2000-01-01 00:00:00`},
{expr: `"2000-01-01 00:00:00" < time`, min: `2000-01-01 00:00:00.000001`, max: `0001-01-01 00:00:00`},
{expr: `"2000-01-01 00:00:00" <= time`, min: `2000-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
{expr: `'2000-01-01 00:00:00' > time`, min: `0001-01-01 00:00:00`, max: `1999-12-31 23:59:59.999999`},
{expr: `'2000-01-01 00:00:00' >= time`, min: `0001-01-01 00:00:00`, max: `2000-01-01 00:00:00`},
{expr: `'2000-01-01 00:00:00' < time`, min: `2000-01-01 00:00:00.000001`, max: `0001-01-01 00:00:00`},
{expr: `'2000-01-01 00:00:00' <= time`, min: `2000-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
// Equality
{expr: `time = "2000-01-01 00:00:00"`, min: `2000-01-01 00:00:00`, max: `2000-01-01 00:00:00`},
{expr: `time = '2000-01-01 00:00:00'`, min: `2000-01-01 00:00:00`, max: `2000-01-01 00:00:00`},
// Multiple time expressions.
{expr: `time >= "2000-01-01 00:00:00" AND time < "2000-01-02 00:00:00"`, min: `2000-01-01 00:00:00`, max: `2000-01-01 23:59:59.999999`},
{expr: `time >= '2000-01-01 00:00:00' AND time < '2000-01-02 00:00:00'`, min: `2000-01-01 00:00:00`, max: `2000-01-01 23:59:59.999999`},
// Min/max crossover
{expr: `time >= "2000-01-01 00:00:00" AND time <= "1999-01-01 00:00:00"`, min: `2000-01-01 00:00:00`, max: `1999-01-01 00:00:00`},
{expr: `time >= '2000-01-01 00:00:00' AND time <= '1999-01-01 00:00:00'`, min: `2000-01-01 00:00:00`, max: `1999-01-01 00:00:00`},
// Absolute time
{expr: `time = 1388534400s`, min: `2014-01-01 00:00:00`, max: `2014-01-01 00:00:00`},
@ -201,8 +201,8 @@ func TestTimeRange(t *testing.T) {
// Non-comparative expressions.
{expr: `time`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
{expr: `time + 2`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
{expr: `time - "2000-01-01 00:00:00"`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
{expr: `time AND "2000-01-01 00:00:00"`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
{expr: `time - '2000-01-01 00:00:00'`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
{expr: `time AND '2000-01-01 00:00:00'`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`},
} {
// Extract time range.
expr := MustParseExpr(tt.expr)

View File

@ -198,9 +198,9 @@ func TestPlanner_Plan_Join(t *testing.T) {
// Query must join the series and sum the values.
rs := db.MustPlanAndExecute(`
SELECT sum(cpu.0.value) + sum(cpu.1.value) AS "sum"
SELECT sum(cpu.0.value) + sum(cpu.1.value) AS sum
FROM JOIN(cpu.0, cpu.1)
WHERE time >= "2000-01-01 00:00:00" AND time < "2000-01-01 00:01:00"
WHERE time >= '2000-01-01 00:00:00' AND time < '2000-01-01 00:01:00'
GROUP BY time(10s)`)
// Expected resultset.

View File

@ -192,7 +192,7 @@ func (p *Parser) parseCreateRetentionPolicyStatement() (*CreateRetentionPolicySt
stmt := &CreateRetentionPolicyStatement{}
// Parse the retention policy name.
ident, err := p.parseIdentifier()
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
@ -204,7 +204,7 @@ func (p *Parser) parseCreateRetentionPolicyStatement() (*CreateRetentionPolicySt
}
// Parse the database name.
ident, err = p.parseIdentifier()
ident, err = p.parseIdent()
if err != nil {
return nil, err
}
@ -251,7 +251,7 @@ func (p *Parser) parseAlterRetentionPolicyStatement() (*AlterRetentionPolicyStat
stmt := &AlterRetentionPolicyStatement{}
// Parse the retention policy name.
ident, err := p.parseIdentifier()
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
@ -263,7 +263,7 @@ func (p *Parser) parseAlterRetentionPolicyStatement() (*AlterRetentionPolicyStat
}
// Parse the database name.
ident, err = p.parseIdentifier()
ident, err = p.parseIdent()
if err != nil {
return nil, err
}
@ -342,15 +342,24 @@ func (p *Parser) parseDuration() (time.Duration, error) {
return d, nil
}
// parserIdentifier parses a string and returns an identifier.
func (p *Parser) parseIdentifier() (string, error) {
// parserIdent parses an identifier.
func (p *Parser) parseIdent() (string, error) {
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
if tok != IDENT {
return "", newParseError(tokstr(tok, lit), []string{"identifier"}, pos)
}
return lit, nil
}
// parserString parses a string.
func (p *Parser) parseString() (string, error) {
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != STRING {
return "", newParseError(tokstr(tok, lit), []string{"string"}, pos)
}
return lit, nil
}
// parseRevokeStatement parses a string and returns a revoke statement.
// This function assumes the REVOKE token has already been consumend.
func (p *Parser) parseRevokeStatement() (*RevokeStatement, error) {
@ -367,9 +376,9 @@ func (p *Parser) parseRevokeStatement() (*RevokeStatement, error) {
tok, pos, lit := p.scanIgnoreWhitespace()
if tok == ON {
// Parse the name of the thing we're granting a privilege to use.
tok, pos, lit = p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
lit, err := p.parseIdent()
if err != nil {
return nil, err
}
stmt.On = lit
@ -386,9 +395,9 @@ func (p *Parser) parseRevokeStatement() (*RevokeStatement, error) {
}
// Parse the name of the user we're granting the privilege to.
tok, pos, lit = p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
lit, err = p.parseIdent()
if err != nil {
return nil, err
}
stmt.User = lit
@ -411,9 +420,9 @@ func (p *Parser) parseGrantStatement() (*GrantStatement, error) {
tok, pos, lit := p.scanIgnoreWhitespace()
if tok == ON {
// Parse the name of the thing we're granting a privilege to use.
tok, pos, lit = p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
lit, err := p.parseIdent()
if err != nil {
return nil, err
}
stmt.On = lit
@ -430,9 +439,9 @@ func (p *Parser) parseGrantStatement() (*GrantStatement, error) {
}
// Parse the name of the user we're granting the privilege to.
tok, pos, lit = p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
lit, err = p.parseIdent()
if err != nil {
return nil, err
}
stmt.User = lit
@ -538,27 +547,12 @@ func (p *Parser) parseTarget(tr targetRequirement) (*Target, error) {
}
// Parse identifier. Could be policy or measurement name.
ident, err := p.parseIdentifier()
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
target := &Target{}
tok, _, _ := p.scanIgnoreWhitespace()
if tok == DOT {
// Previous identifier was retention policy name.
target.RetentionPolicy = ident
// Parse required measurement.
ident, err = p.parseIdentifier()
if err != nil {
return nil, err
}
} else {
p.unscan()
}
target.Measurement = ident
// Parse optional ON.
@ -568,7 +562,7 @@ func (p *Parser) parseTarget(tr targetRequirement) (*Target, error) {
}
// Found an ON token so parse required identifier.
if ident, err = p.parseIdentifier(); err != nil {
if ident, err = p.parseIdent(); err != nil {
return nil, err
}
target.Database = ident
@ -664,7 +658,7 @@ func (p *Parser) parseListMeasurementsStatement() (*ListMeasurementsStatement, e
func (p *Parser) parseListRetentionPoliciesStatement() (*ListRetentionPoliciesStatement, error) {
stmt := &ListRetentionPoliciesStatement{}
ident, err := p.parseIdentifier()
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
@ -841,9 +835,9 @@ func (p *Parser) parseDropSeriesStatement() (*DropSeriesStatement, error) {
stmt := &DropSeriesStatement{}
// Read the name of the series to drop.
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
lit, err := p.parseIdent()
if err != nil {
return nil, err
}
stmt.Name = lit
@ -881,7 +875,7 @@ func (p *Parser) parseCreateContinuousQueryStatement() (*CreateContinuousQuerySt
}
// Read the id of the query to create.
ident, err := p.parseIdentifier()
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
@ -893,7 +887,7 @@ func (p *Parser) parseCreateContinuousQueryStatement() (*CreateContinuousQuerySt
}
// Read the name of the database to create the query on.
if ident, err = p.parseIdentifier(); err != nil {
if ident, err = p.parseIdent(); err != nil {
return nil, err
}
stmt.Database = ident
@ -924,9 +918,9 @@ func (p *Parser) parseCreateDatabaseStatement() (*CreateDatabaseStatement, error
stmt := &CreateDatabaseStatement{}
// Parse the name of the database to be created.
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier"}, pos)
lit, err := p.parseIdent()
if err != nil {
return nil, err
}
stmt.Name = lit
@ -939,9 +933,9 @@ func (p *Parser) parseDropDatabaseStatement() (*DropDatabaseStatement, error) {
stmt := &DropDatabaseStatement{}
// Parse the name of the database to be dropped.
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier"}, pos)
lit, err := p.parseIdent()
if err != nil {
return nil, err
}
stmt.Name = lit
@ -954,7 +948,7 @@ func (p *Parser) parseDropRetentionPolicyStatement() (*DropRetentionPolicyStatem
stmt := &DropRetentionPolicyStatement{}
// Parse the policy name.
ident, err := p.parseIdentifier()
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
@ -966,7 +960,7 @@ func (p *Parser) parseDropRetentionPolicyStatement() (*DropRetentionPolicyStatem
}
// Parse the database name.
if stmt.Database, err = p.parseIdentifier(); err != nil {
if stmt.Database, err = p.parseIdent(); err != nil {
return nil, err
}
@ -979,7 +973,7 @@ func (p *Parser) parseCreateUserStatement() (*CreateUserStatement, error) {
stmt := &CreateUserStatement{}
// Parse name of the user to be created.
ident, err := p.parseIdentifier()
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
@ -991,7 +985,7 @@ func (p *Parser) parseCreateUserStatement() (*CreateUserStatement, error) {
}
// Parse new user's password
if ident, err = p.parseIdentifier(); err != nil {
if ident, err = p.parseString(); err != nil {
return nil, err
}
stmt.Password = ident
@ -1018,9 +1012,9 @@ func (p *Parser) parseDropUserStatement() (*DropUserStatement, error) {
stmt := &DropUserStatement{}
// Parse the name of the user to be dropped.
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier"}, pos)
lit, err := p.parseIdent()
if err != nil {
return nil, err
}
stmt.Name = lit
@ -1050,9 +1044,8 @@ func (p *Parser) parseRetentionPolicy() (name string, dfault bool, err error) {
}
// Parse retention policy name.
tok, pos, name = p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
err = newParseError(tokstr(tok, name), []string{"identifier"}, pos)
name, err = p.parseIdent()
if err != nil {
return
}
@ -1070,9 +1063,9 @@ func (p *Parser) parseDropContinuousQueryStatement() (*DropContinuousQueryStatem
}
// Read the id of the query to drop.
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
lit, err := p.parseIdent()
if err != nil {
return nil, err
}
stmt.Name = lit
@ -1142,9 +1135,9 @@ func (p *Parser) parseAlias() (string, error) {
}
// Then we should have the alias identifier.
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return "", newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
lit, err := p.parseIdent()
if err != nil {
return "", err
}
return lit, nil
}
@ -1153,8 +1146,8 @@ func (p *Parser) parseAlias() (string, error) {
func (p *Parser) parseSource() (Source, error) {
// The first token can either be the series name or a join/merge call.
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
if tok != IDENT {
return nil, newParseError(tokstr(tok, lit), []string{"identifier"}, pos)
}
// If the token is a string or the next token is not an LPAREN then return a measurement.
@ -1174,7 +1167,7 @@ func (p *Parser) parseSource() (Source, error) {
for {
// Scan the measurement name.
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
if tok != IDENT {
return nil, newParseError(tokstr(tok, lit), []string{"measurement name"}, pos)
}
measurements = append(measurements, &Measurement{Name: lit})
@ -1614,15 +1607,19 @@ func (p *Parser) parseTokens(toks []Token) error {
}
// Quote returns a quoted string.
func Quote(s string) string {
return `"` + strings.NewReplacer("\n", `\n`, `\`, `\\`, `"`, `\"`).Replace(s) + `"`
func QuoteString(s string) string {
return `'` + strings.NewReplacer("\n", `\n`, `\`, `\\`, `'`, `\'`).Replace(s) + `'`
}
// QuoteIdent returns a quoted identifier from multiple bare identifiers.
func QuoteIdent(segments []string) string {
r := strings.NewReplacer("\n", `\n`, `\`, `\\`, `"`, `\"`)
var buf bytes.Buffer
for i, segment := range segments {
_, _ = buf.WriteString(Quote(segment))
_ = buf.WriteByte('"')
_, _ = buf.WriteString(r.Replace(segment))
_ = buf.WriteByte('"')
if i < len(segments)-1 {
_ = buf.WriteByte('.')
}

View File

@ -89,7 +89,7 @@ func TestParser_ParseStatement(t *testing.T) {
Source: &influxql.Join{
Measurements: []*influxql.Measurement{
{Name: "aa"},
{Name: "bb"},
{Name: `"bb"`},
{Name: "cc"},
},
},
@ -320,8 +320,7 @@ func TestParser_ParseStatement(t *testing.T) {
Source: &influxql.SelectStatement{
Fields: []*influxql.Field{&influxql.Field{Expr: &influxql.Call{Name: "count"}}},
Target: &influxql.Target{
RetentionPolicy: "1h.policy1",
Measurement: "cpu.load",
Measurement: `"1h.policy1"."cpu.load"`,
},
Source: &influxql.Measurement{Name: "myseries"},
},
@ -338,7 +337,7 @@ func TestParser_ParseStatement(t *testing.T) {
// CREATE USER statement
{
s: `CREATE USER testuser WITH PASSWORD pwd1337`,
s: `CREATE USER testuser WITH PASSWORD 'pwd1337'`,
stmt: &influxql.CreateUserStatement{
Name: "testuser",
Password: "pwd1337",
@ -347,7 +346,7 @@ func TestParser_ParseStatement(t *testing.T) {
// CREATE USER ... WITH ALL PRIVILEGES
{
s: `CREATE USER testuser WITH PASSWORD pwd1337 WITH ALL PRIVILEGES`,
s: `CREATE USER testuser WITH PASSWORD 'pwd1337' WITH ALL PRIVILEGES`,
stmt: &influxql.CreateUserStatement{
Name: "testuser",
Password: "pwd1337",
@ -371,8 +370,8 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `DROP RETENTION POLICY "1h.cpu" ON mydb`,
stmt: &influxql.DropRetentionPolicyStatement{
Name: "1h.cpu",
Database: "mydb",
Name: `"1h.cpu"`,
Database: `mydb`,
},
},
@ -546,46 +545,46 @@ func TestParser_ParseStatement(t *testing.T) {
{s: `SELECT field1 FROM myseries ORDER`, err: `found EOF, expected BY at line 1, char 35`},
{s: `SELECT field1 FROM myseries ORDER BY /`, err: `found /, expected identifier, ASC, or DESC at line 1, char 38`},
{s: `SELECT field1 FROM myseries ORDER BY 1`, err: `found 1, expected identifier, ASC, or DESC at line 1, char 38`},
{s: `SELECT field1 AS`, err: `found EOF, expected identifier, string at line 1, char 18`},
{s: `SELECT field1 FROM 12`, err: `found 12, expected identifier, string at line 1, char 20`},
{s: `SELECT field1 AS`, err: `found EOF, expected identifier at line 1, char 18`},
{s: `SELECT field1 FROM 12`, err: `found 12, expected identifier at line 1, char 20`},
{s: `SELECT field1 FROM myseries GROUP BY *`, err: `found *, expected identifier, string, number, bool at line 1, char 38`},
{s: `SELECT 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 FROM myseries`, err: `unable to parse number at line 1, char 8`},
{s: `SELECT 10.5h FROM myseries`, err: `found h, expected FROM at line 1, char 12`},
{s: `DELETE`, err: `found EOF, expected FROM at line 1, char 8`},
{s: `DELETE FROM`, err: `found EOF, expected identifier, string at line 1, char 13`},
{s: `DELETE FROM`, err: `found EOF, expected identifier at line 1, char 13`},
{s: `DELETE FROM myseries WHERE`, err: `found EOF, expected identifier, string, number, bool at line 1, char 28`},
{s: `DROP SERIES`, err: `found EOF, expected identifier, string at line 1, char 13`},
{s: `DROP SERIES`, err: `found EOF, expected identifier at line 1, char 13`},
{s: `LIST CONTINUOUS`, err: `found EOF, expected QUERIES at line 1, char 17`},
{s: `LIST RETENTION`, err: `found EOF, expected POLICIES at line 1, char 16`},
{s: `LIST RETENTION POLICIES`, err: `found EOF, expected identifier at line 1, char 25`},
{s: `LIST FOO`, err: `found FOO, expected SERIES, CONTINUOUS, MEASUREMENTS, TAG, FIELD, RETENTION at line 1, char 6`},
{s: `DROP CONTINUOUS`, err: `found EOF, expected QUERY at line 1, char 17`},
{s: `DROP CONTINUOUS QUERY`, err: `found EOF, expected identifier, string at line 1, char 23`},
{s: `DROP CONTINUOUS QUERY`, err: `found EOF, expected identifier at line 1, char 23`},
{s: `DROP FOO`, err: `found FOO, expected SERIES, CONTINUOUS at line 1, char 6`},
{s: `DROP DATABASE`, err: `found EOF, expected identifier at line 1, char 15`},
{s: `DROP RETENTION`, err: `found EOF, expected POLICY at line 1, char 16`},
{s: `DROP RETENTION POLICY`, err: `found EOF, expected identifier at line 1, char 23`},
{s: `DROP RETENTION POLICY "1h.cpu"`, err: `found EOF, expected ON at line 1, char 31`},
{s: `DROP RETENTION POLICY "1h.cpu"`, err: `found EOF, expected ON at line 1, char 32`},
{s: `DROP RETENTION POLICY "1h.cpu" ON`, err: `found EOF, expected identifier at line 1, char 35`},
{s: `DROP USER`, err: `found EOF, expected identifier at line 1, char 11`},
{s: `CREATE USER testuser`, err: `found EOF, expected WITH at line 1, char 22`},
{s: `CREATE USER testuser WITH`, err: `found EOF, expected PASSWORD at line 1, char 27`},
{s: `CREATE USER testuser WITH PASSWORD`, err: `found EOF, expected identifier at line 1, char 36`},
{s: `CREATE USER testuser WITH PASSWORD "pwd" WITH`, err: `found EOF, expected ALL at line 1, char 47`},
{s: `CREATE USER testuser WITH PASSWORD "pwd" WITH ALL`, err: `found EOF, expected PRIVILEGES at line 1, char 51`},
{s: `CREATE USER testuser WITH PASSWORD`, err: `found EOF, expected string at line 1, char 36`},
{s: `CREATE USER testuser WITH PASSWORD 'pwd' WITH`, err: `found EOF, expected ALL at line 1, char 47`},
{s: `CREATE USER testuser WITH PASSWORD 'pwd' WITH ALL`, err: `found EOF, expected PRIVILEGES at line 1, char 51`},
{s: `GRANT`, err: `found EOF, expected READ, WRITE, ALL [PRIVILEGES] at line 1, char 7`},
{s: `GRANT BOGUS`, err: `found BOGUS, expected READ, WRITE, ALL [PRIVILEGES] at line 1, char 7`},
{s: `GRANT READ`, err: `found EOF, expected ON at line 1, char 12`},
{s: `GRANT READ TO jdoe`, err: `found TO, expected ON at line 1, char 12`},
{s: `GRANT READ ON`, err: `found EOF, expected identifier, string at line 1, char 15`},
{s: `GRANT READ ON`, err: `found EOF, expected identifier at line 1, char 15`},
{s: `GRANT READ ON testdb`, err: `found EOF, expected TO at line 1, char 22`},
{s: `GRANT READ ON testdb TO`, err: `found EOF, expected identifier, string at line 1, char 25`}, {s: `GRANT`, err: `found EOF, expected READ, WRITE, ALL [PRIVILEGES] at line 1, char 7`},
{s: `GRANT READ ON testdb TO`, err: `found EOF, expected identifier at line 1, char 25`}, {s: `GRANT`, err: `found EOF, expected READ, WRITE, ALL [PRIVILEGES] at line 1, char 7`},
{s: `REVOKE BOGUS`, err: `found BOGUS, expected READ, WRITE, ALL [PRIVILEGES] at line 1, char 8`},
{s: `REVOKE READ`, err: `found EOF, expected ON at line 1, char 13`},
{s: `REVOKE READ TO jdoe`, err: `found TO, expected ON at line 1, char 13`},
{s: `REVOKE READ ON`, err: `found EOF, expected identifier, string at line 1, char 16`},
{s: `REVOKE READ ON`, err: `found EOF, expected identifier at line 1, char 16`},
{s: `REVOKE READ ON testdb`, err: `found EOF, expected FROM at line 1, char 23`},
{s: `REVOKE READ ON testdb FROM`, err: `found EOF, expected identifier, string at line 1, char 28`},
{s: `REVOKE READ ON testdb FROM`, err: `found EOF, expected identifier at line 1, char 28`},
{s: `CREATE RETENTION`, err: `found EOF, expected POLICY at line 1, char 18`},
{s: `CREATE RETENTION POLICY`, err: `found EOF, expected identifier at line 1, char 25`},
{s: `CREATE RETENTION POLICY policy1`, err: `found EOF, expected ON at line 1, char 33`},
@ -612,10 +611,6 @@ func TestParser_ParseStatement(t *testing.T) {
t.Errorf("%d. %q: error mismatch:\n exp=%s\n got=%s\n\n", i, tt.s, tt.err, err)
} else if tt.err == "" && !reflect.DeepEqual(tt.stmt, stmt) {
t.Errorf("%d. %q\n\nstmt mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.s, tt.stmt, stmt)
exp := tt.stmt.(*influxql.CreateContinuousQueryStatement).Source.Target
got := stmt.(*influxql.CreateContinuousQueryStatement).Source.Target
t.Errorf("exp.String() = %#v\n", *exp)
t.Errorf("got.String() = %#v\n", *got)
}
}
}
@ -629,14 +624,14 @@ func TestParser_ParseExpr(t *testing.T) {
}{
// Primitives
{s: `100`, expr: &influxql.NumberLiteral{Val: 100}},
{s: `"foo bar"`, expr: &influxql.StringLiteral{Val: "foo bar"}},
{s: `'foo bar'`, expr: &influxql.StringLiteral{Val: "foo bar"}},
{s: `true`, expr: &influxql.BooleanLiteral{Val: true}},
{s: `false`, expr: &influxql.BooleanLiteral{Val: false}},
{s: `my_ident`, expr: &influxql.VarRef{Val: "my_ident"}},
{s: `"2000-01-01 00:00:00"`, expr: &influxql.TimeLiteral{Val: mustParseTime("2000-01-01T00:00:00Z")}},
{s: `"2000-01-32 00:00:00"`, err: `unable to parse datetime at line 1, char 1`},
{s: `"2000-01-01"`, expr: &influxql.TimeLiteral{Val: mustParseTime("2000-01-01T00:00:00Z")}},
{s: `"2000-01-99"`, err: `unable to parse date at line 1, char 1`},
{s: `'2000-01-01 00:00:00'`, expr: &influxql.TimeLiteral{Val: mustParseTime("2000-01-01T00:00:00Z")}},
{s: `'2000-01-32 00:00:00'`, err: `unable to parse datetime at line 1, char 1`},
{s: `'2000-01-01'`, expr: &influxql.TimeLiteral{Val: mustParseTime("2000-01-01T00:00:00Z")}},
{s: `'2000-01-99'`, err: `unable to parse date at line 1, char 1`},
// Simple binary expression
{
@ -831,13 +826,13 @@ func TestQuote(t *testing.T) {
in string
out string
}{
{``, `""`},
{`foo`, `"foo"`},
{"foo\nbar", `"foo\nbar"`},
{`foo bar\\`, `"foo bar\\\\"`},
{`"foo"`, `"\"foo\""`},
{``, `''`},
{`foo`, `'foo'`},
{"foo\nbar", `'foo\nbar'`},
{`foo bar\\`, `'foo bar\\\\'`},
{`'foo'`, `'\'foo\''`},
} {
if out := influxql.Quote(tt.in); tt.out != out {
if out := influxql.QuoteString(tt.in); tt.out != out {
t.Errorf("%d. %s: mismatch: %s != %s", i, tt.in, tt.out, out)
}
}

View File

@ -42,7 +42,10 @@ func (s *Scanner) Scan() (tok Token, pos Pos, lit string) {
switch ch0 {
case eof:
return EOF, pos, ""
case '"', '\'':
case '"':
s.r.unread()
return s.scanIdent()
case '\'':
return s.scanString()
case '.':
ch1, _ := s.r.read()
@ -123,11 +126,12 @@ func (s *Scanner) scanIdent() (tok Token, pos Pos, lit string) {
} else if ch == '.' {
buf.WriteRune(ch)
} else if ch == '"' {
s.r.unread()
if tok0, pos0, lit0 := s.scanString(); tok == BADSTRING || tok == BADESCAPE {
return tok0, pos0, lit0
} else {
buf.WriteString(lit0)
_ = buf.WriteByte('"')
_, _ = buf.WriteString(lit0)
_ = buf.WriteByte('"')
}
} else if isIdentChar(ch) {
s.r.unread()

View File

@ -59,17 +59,18 @@ func TestScanner_Scan(t *testing.T) {
// Identifiers
{s: `foo`, tok: influxql.IDENT, lit: `foo`},
{s: `Zx12_3U_-`, tok: influxql.IDENT, lit: `Zx12_3U_`},
{s: `"foo".bar`, tok: influxql.IDENT, lit: `"foo".bar`},
{s: `true`, tok: influxql.TRUE},
{s: `false`, tok: influxql.FALSE},
// Strings
{s: `"testing 123!"`, tok: influxql.STRING, lit: `testing 123!`},
{s: `"foo\nbar"`, tok: influxql.STRING, lit: "foo\nbar"},
{s: `"foo\\bar"`, tok: influxql.STRING, lit: "foo\\bar"},
{s: `"test`, tok: influxql.BADSTRING, lit: `test`},
{s: "\"test\nfoo", tok: influxql.BADSTRING, lit: `test`},
{s: `"test\g"`, tok: influxql.BADESCAPE, lit: `\g`, pos: influxql.Pos{Line: 0, Char: 6}},
{s: `'testing 123!'`, tok: influxql.STRING, lit: `testing 123!`},
{s: `'foo\nbar'`, tok: influxql.STRING, lit: "foo\nbar"},
{s: `'foo\\bar'`, tok: influxql.STRING, lit: "foo\\bar"},
{s: `'test`, tok: influxql.BADSTRING, lit: `test`},
{s: "'test\nfoo", tok: influxql.BADSTRING, lit: `test`},
{s: `'test\g'`, tok: influxql.BADESCAPE, lit: `\g`, pos: influxql.Pos{Line: 0, Char: 6}},
// Numbers
{s: `100`, tok: influxql.NUMBER, lit: `100`},
@ -192,7 +193,7 @@ func TestScanner_Scan_Multi(t *testing.T) {
}
// Create a scanner.
v := `SELECT value from myseries WHERE a = "b"`
v := `SELECT value from myseries WHERE a = 'b'`
s := influxql.NewScanner(strings.NewReader(v))
// Continually scan until we reach the end.

View File

@ -1752,7 +1752,7 @@ func (s *Server) normalizeStatement(stmt influxql.Statement, defaultDatabase str
case *influxql.VarRef:
for k, v := range prefixes {
if strings.HasPrefix(n.Val, k+".") {
n.Val = v + "." + influxql.Quote(n.Val[len(k)+1:])
n.Val = v + "." + influxql.QuoteIdent([]string{n.Val[len(k)+1:]})
}
}
}