Skip to content

Commit

Permalink
Merge pull request #4866 from pires/2676-createdb_with_retention
Browse files Browse the repository at this point in the history
Added support for configurable default retention policy on database creation
  • Loading branch information
pauldix committed Dec 2, 2015
2 parents e5b9ac9 + 30cc133 commit 218ef13
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 34 deletions.
44 changes: 42 additions & 2 deletions cmd/influxd/run/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,46 @@ func TestServer_DatabaseCommands(t *testing.T) {
command: `CREATE DATABASE db0`,
exp: `{"results":[{}]}`,
},
&Query{
name: "create database with retention duration should succeed",
command: `CREATE DATABASE db0_r WITH DURATION 24h REPLICATION 2 NAME db0_r_policy`,
exp: `{"results":[{}]}`,
},
&Query{
name: "create database should error with bad name",
command: `CREATE DATABASE 0xdb0`,
exp: `{"error":"error parsing query: found 0, expected identifier at line 1, char 17"}`,
},
&Query{
name: "create database with retention duration should error with bad retention duration",
command: `CREATE DATABASE db0 WITH DURATION xyz`,
exp: `{"error":"error parsing query: found xyz, expected duration at line 1, char 35"}`,
},
&Query{
name: "create database with retention replication should error with bad retention replication number",
command: `CREATE DATABASE db0 WITH REPLICATION xyz`,
exp: `{"error":"error parsing query: found xyz, expected number at line 1, char 38"}`,
},
&Query{
name: "create database with retention name should error with missing retention name",
command: `CREATE DATABASE db0 WITH NAME`,
exp: `{"error":"error parsing query: found EOF, expected identifier at line 1, char 31"}`,
},
&Query{
name: "show database should succeed",
command: `SHOW DATABASES`,
exp: `{"results":[{"series":[{"name":"databases","columns":["name"],"values":[["db0"]]}]}]}`,
exp: `{"results":[{"series":[{"name":"databases","columns":["name"],"values":[["db0"],["db0_r"]]}]}]}`,
},
&Query{
name: "create database should error if it already exists",
command: `CREATE DATABASE db0`,
exp: `{"results":[{"error":"database already exists"}]}`,
},
&Query{
name: "create database should error if it already exists",
command: `CREATE DATABASE db0_r`,
exp: `{"results":[{"error":"database already exists"}]}`,
},
&Query{
name: "create database should not error with existing database with IF NOT EXISTS",
command: `CREATE DATABASE IF NOT EXISTS db0`,
Expand All @@ -63,16 +88,31 @@ func TestServer_DatabaseCommands(t *testing.T) {
command: `CREATE DATABASE IF NOT EXISTS db1`,
exp: `{"results":[{}]}`,
},
&Query{
name: "create database with retention duration should not error with existing database with IF NOT EXISTS",
command: `CREATE DATABASE IF NOT EXISTS db1 WITH DURATION 24h`,
exp: `{"results":[{}]}`,
},
&Query{
name: "create database should error IF NOT EXISTS with bad retention duration",
command: `CREATE DATABASE IF NOT EXISTS db1 WITH DURATION xyz`,
exp: `{"error":"error parsing query: found xyz, expected duration at line 1, char 49"}`,
},
&Query{
name: "show database should succeed",
command: `SHOW DATABASES`,
exp: `{"results":[{"series":[{"name":"databases","columns":["name"],"values":[["db0"],["db1"]]}]}]}`,
exp: `{"results":[{"series":[{"name":"databases","columns":["name"],"values":[["db0"],["db0_r"],["db1"]]}]}]}`,
},
&Query{
name: "drop database db0 should succeed",
command: `DROP DATABASE db0`,
exp: `{"results":[{}]}`,
},
&Query{
name: "drop database db0_r should succeed",
command: `DROP DATABASE db0_r`,
exp: `{"results":[{}]}`,
},
&Query{
name: "drop database db1 should succeed",
command: `DROP DATABASE db1`,
Expand Down
21 changes: 21 additions & 0 deletions influxql/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,18 @@ type CreateDatabaseStatement struct {
// IfNotExists indicates whether to return without error if the database
// already exists.
IfNotExists bool

// RetentionPolicyCreate indicates whether the user explicitly wants to create a retention policy
RetentionPolicyCreate bool

// RetentionPolicyDuration indicates retention duration for the new database
RetentionPolicyDuration time.Duration

// RetentionPolicyReplication indicates retention replication for the new database
RetentionPolicyReplication int

// RetentionPolicyName indicates retention name for the new database
RetentionPolicyName string
}

// String returns a string representation of the create database statement.
Expand All @@ -330,6 +342,15 @@ func (s *CreateDatabaseStatement) String() string {
_, _ = buf.WriteString("IF NOT EXISTS ")
}
_, _ = buf.WriteString(QuoteIdent(s.Name))
if s.RetentionPolicyCreate {
_, _ = buf.WriteString("WITH DURATION ")
_, _ = buf.WriteString(s.RetentionPolicyDuration.String())
_, _ = buf.WriteString("REPLICATION ")
_, _ = buf.WriteString(strconv.Itoa(s.RetentionPolicyReplication))
_, _ = buf.WriteString("NAME ")
_, _ = buf.WriteString(QuoteIdent(s.RetentionPolicyName))
}

return buf.String()
}

Expand Down
50 changes: 50 additions & 0 deletions influxql/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,56 @@ func (p *Parser) parseCreateDatabaseStatement() (*CreateDatabaseStatement, error
}
stmt.Name = lit

// Look for "WITH"
if tok, _, _ := p.scanIgnoreWhitespace(); tok == WITH {
// validate that at least one of DURATION, REPLICATION or NAME is provided
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != DURATION && tok != REPLICATION && tok != NAME {
return nil, newParseError(tokstr(tok, lit), []string{"DURATION", "REPLICATION", "NAME"}, pos)
}
// rewind
p.unscan()

// mark statement as having a RetentionPolicyInfo defined
stmt.RetentionPolicyCreate = true

// Look for "DURATION"
var rpDuration time.Duration // default is forever
if err := p.parseTokens([]Token{DURATION}); err != nil {
p.unscan()
} else {
rpDuration, err = p.parseDuration()
if err != nil {
return nil, err
}
}
stmt.RetentionPolicyDuration = rpDuration

// Look for "REPLICATION"
var rpReplication int = 1 // default is 1
if err := p.parseTokens([]Token{REPLICATION}); err != nil {
p.unscan()
} else {
rpReplication, err = p.parseInt(1, math.MaxInt32)
if err != nil {
return nil, err
}
}
stmt.RetentionPolicyReplication = rpReplication

// Look for "NAME"
var rpName string = "default" // default is default
if err := p.parseTokens([]Token{NAME}); err == nil {
rpName, err = p.parseIdent()
if err != nil {
return nil, err
}
}
stmt.RetentionPolicyName = rpName
} else {
p.unscan()
}

return stmt, nil
}

Expand Down
106 changes: 102 additions & 4 deletions influxql/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1176,15 +1176,105 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `CREATE DATABASE testdb`,
stmt: &influxql.CreateDatabaseStatement{
Name: "testdb",
IfNotExists: false,
Name: "testdb",
IfNotExists: false,
RetentionPolicyCreate: false,
},
},
{
s: `CREATE DATABASE IF NOT EXISTS testdb`,
stmt: &influxql.CreateDatabaseStatement{
Name: "testdb",
IfNotExists: true,
Name: "testdb",
IfNotExists: true,
RetentionPolicyCreate: false,
},
},
{
s: `CREATE DATABASE testdb WITH DURATION 24h`,
stmt: &influxql.CreateDatabaseStatement{
Name: "testdb",
IfNotExists: false,
RetentionPolicyCreate: true,
RetentionPolicyDuration: 24 * time.Hour,
RetentionPolicyReplication: 1,
RetentionPolicyName: "default",
},
},
{
s: `CREATE DATABASE IF NOT EXISTS testdb WITH DURATION 24h`,
stmt: &influxql.CreateDatabaseStatement{
Name: "testdb",
IfNotExists: true,
RetentionPolicyCreate: true,
RetentionPolicyDuration: 24 * time.Hour,
RetentionPolicyReplication: 1,
RetentionPolicyName: "default",
},
},
{
s: `CREATE DATABASE testdb WITH REPLICATION 2`,
stmt: &influxql.CreateDatabaseStatement{
Name: "testdb",
IfNotExists: false,
RetentionPolicyCreate: true,
RetentionPolicyDuration: 0,
RetentionPolicyReplication: 2,
RetentionPolicyName: "default",
},
},
{
s: `CREATE DATABASE IF NOT EXISTS testdb WITH REPLICATION 2`,
stmt: &influxql.CreateDatabaseStatement{
Name: "testdb",
IfNotExists: true,
RetentionPolicyCreate: true,
RetentionPolicyDuration: 0,
RetentionPolicyReplication: 2,
RetentionPolicyName: "default",
},
},
{
s: `CREATE DATABASE testdb WITH NAME test_name`,
stmt: &influxql.CreateDatabaseStatement{
Name: "testdb",
IfNotExists: false,
RetentionPolicyCreate: true,
RetentionPolicyDuration: 0,
RetentionPolicyReplication: 1,
RetentionPolicyName: "test_name",
},
},
{
s: `CREATE DATABASE IF NOT EXISTS testdb WITH NAME test_name`,
stmt: &influxql.CreateDatabaseStatement{
Name: "testdb",
IfNotExists: true,
RetentionPolicyCreate: true,
RetentionPolicyDuration: 0,
RetentionPolicyReplication: 1,
RetentionPolicyName: "test_name",
},
},
{
s: `CREATE DATABASE testdb WITH DURATION 24h REPLICATION 2 NAME test_name`,
stmt: &influxql.CreateDatabaseStatement{
Name: "testdb",
IfNotExists: false,
RetentionPolicyCreate: true,
RetentionPolicyDuration: 24 * time.Hour,
RetentionPolicyReplication: 2,
RetentionPolicyName: "test_name",
},
},
{
s: `CREATE DATABASE IF NOT EXISTS testdb WITH DURATION 24h REPLICATION 2 NAME test_name`,
stmt: &influxql.CreateDatabaseStatement{
Name: "testdb",
IfNotExists: true,
RetentionPolicyCreate: true,
RetentionPolicyDuration: 24 * time.Hour,
RetentionPolicyReplication: 2,
RetentionPolicyName: "test_name",
},
},

Expand Down Expand Up @@ -1612,9 +1702,17 @@ func TestParser_ParseStatement(t *testing.T) {
{s: `DROP FOO`, err: `found FOO, expected SERIES, CONTINUOUS, MEASUREMENT, SERVER, SUBSCRIPTION at line 1, char 6`},
{s: `CREATE FOO`, err: `found FOO, expected CONTINUOUS, DATABASE, USER, RETENTION, SUBSCRIPTION at line 1, char 8`},
{s: `CREATE DATABASE`, err: `found EOF, expected identifier at line 1, char 17`},
{s: `CREATE DATABASE "testdb" WITH`, err: `found EOF, expected DURATION, REPLICATION, NAME at line 1, char 31`},
{s: `CREATE DATABASE "testdb" WITH DURATION`, err: `found EOF, expected duration at line 1, char 40`},
{s: `CREATE DATABASE "testdb" WITH REPLICATION`, err: `found EOF, expected number at line 1, char 43`},
{s: `CREATE DATABASE "testdb" WITH NAME`, err: `found EOF, expected identifier at line 1, char 36`},
{s: `CREATE DATABASE IF`, err: `found EOF, expected NOT at line 1, char 20`},
{s: `CREATE DATABASE IF NOT`, err: `found EOF, expected EXISTS at line 1, char 24`},
{s: `CREATE DATABASE IF NOT EXISTS`, err: `found EOF, expected identifier at line 1, char 31`},
{s: `CREATE DATABASE IF NOT EXISTS "testdb" WITH`, err: `found EOF, expected DURATION, REPLICATION, NAME at line 1, char 45`},
{s: `CREATE DATABASE IF NOT EXISTS "testdb" WITH DURATION`, err: `found EOF, expected duration at line 1, char 54`},
{s: `CREATE DATABASE IF NOT EXISTS "testdb" WITH REPLICATION`, err: `found EOF, expected number at line 1, char 57`},
{s: `CREATE DATABASE IF NOT EXISTS "testdb" WITH NAME`, err: `found EOF, expected identifier at line 1, char 50`},
{s: `DROP DATABASE`, err: `found EOF, expected identifier at line 1, char 15`},
{s: `DROP DATABASE IF`, err: `found EOF, expected EXISTS at line 1, char 18`},
{s: `DROP DATABASE IF EXISTS`, err: `found EOF, expected identifier at line 1, char 25`},
Expand Down
2 changes: 2 additions & 0 deletions influxql/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ const (
LIMIT
MEASUREMENT
MEASUREMENTS
NAME
NOT
OFFSET
ON
Expand Down Expand Up @@ -215,6 +216,7 @@ var tokens = [...]string{
LIMIT: "LIMIT",
MEASUREMENT: "MEASUREMENT",
MEASUREMENTS: "MEASUREMENTS",
NAME: "NAME",
NOT: "NOT",
OFFSET: "OFFSET",
ON: "ON",
Expand Down
12 changes: 11 additions & 1 deletion meta/statement_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type StatementExecutor struct {
Database(name string) (*DatabaseInfo, error)
Databases() ([]DatabaseInfo, error)
CreateDatabase(name string) (*DatabaseInfo, error)
CreateDatabaseWithRetentionPolicy(name string, rpi *RetentionPolicyInfo) (*DatabaseInfo, error)
DropDatabase(name string) error

DefaultRetentionPolicy(database string) (*RetentionPolicyInfo, error)
Expand Down Expand Up @@ -110,10 +111,19 @@ func (e *StatementExecutor) ExecuteStatement(stmt influxql.Statement) *influxql.
}

func (e *StatementExecutor) executeCreateDatabaseStatement(q *influxql.CreateDatabaseStatement) *influxql.Result {
_, err := e.Store.CreateDatabase(q.Name)
var err error
if q.RetentionPolicyCreate {
rpi := NewRetentionPolicyInfo(q.RetentionPolicyName)
rpi.Duration = q.RetentionPolicyDuration
rpi.ReplicaN = q.RetentionPolicyReplication
_, err = e.Store.CreateDatabaseWithRetentionPolicy(q.Name, rpi)
} else {
_, err = e.Store.CreateDatabase(q.Name)
}
if err == ErrDatabaseExists && q.IfNotExists {
err = nil
}

return &influxql.Result{Err: err}
}

Expand Down
Loading

0 comments on commit 218ef13

Please sign in to comment.