Support ON and use default database for SHOW commands

Normalize all of the SHOW commands so they allow both using ON to
specify the database and using the default database. Some commands would
require one and some would require the other and it was confusing when
using the query language.

Affected commands:

* SHOW RETENTION POLICIES
* SHOW MEASUREMENTS
* SHOW SERIES
* SHOW TAG KEYS
* SHOW TAG VALUES
* SHOW FIELD KEYS
pull/7295/head
Jonathan A. Sternberg 2016-09-13 15:36:56 -05:00
parent 0b0c752f5a
commit aae88fc3c3
7 changed files with 249 additions and 23 deletions

View File

@ -10,6 +10,7 @@
- [#7099](https://github.com/influxdata/influxdb/pull/7099): Implement text/csv content encoding for the response writer.
- [#6992](https://github.com/influxdata/influxdb/issues/6992): Support tools for running async queries.
- [#7136](https://github.com/influxdata/influxdb/pull/7136): Update jwt-go dependency to version 3.
- [#6962](https://github.com/influxdata/influxdb/issues/6962): Support ON and use default database for SHOW commands.
### Bugfixes

View File

@ -672,11 +672,11 @@ func (e *StatementExecutor) executeShowGrantsForUserStatement(q *influxql.ShowGr
}
func (e *StatementExecutor) executeShowMeasurementsStatement(q *influxql.ShowMeasurementsStatement, ctx *influxql.ExecutionContext) error {
if ctx.Database == "" {
if q.Database == "" {
return ErrDatabaseNameRequired
}
measurements, err := e.TSDBStore.Measurements(ctx.Database, q.Condition)
measurements, err := e.TSDBStore.Measurements(q.Database, q.Condition)
if err != nil || len(measurements) == 0 {
ctx.Results <- &influxql.Result{
StatementID: ctx.StatementID,
@ -723,6 +723,10 @@ func (e *StatementExecutor) executeShowMeasurementsStatement(q *influxql.ShowMea
}
func (e *StatementExecutor) executeShowRetentionPoliciesStatement(q *influxql.ShowRetentionPoliciesStatement) (models.Rows, error) {
if q.Database == "" {
return nil, ErrDatabaseNameRequired
}
di := e.MetaClient.Database(q.Database)
if di == nil {
return nil, influxdb.ErrDatabaseNotFound(q.Database)
@ -1068,6 +1072,14 @@ func (e *StatementExecutor) NormalizeStatement(stmt influxql.Statement, defaultD
return
}
switch node := node.(type) {
case *influxql.ShowRetentionPoliciesStatement:
if node.Database == "" {
node.Database = defaultDatabase
}
case *influxql.ShowMeasurementsStatement:
if node.Database == "" {
node.Database = defaultDatabase
}
case *influxql.Measurement:
err = e.normalizeMeasurement(node, defaultDatabase)
}

View File

@ -2245,6 +2245,10 @@ func (s *DeleteStatement) RequiredPrivileges() (ExecutionPrivileges, error) {
// ShowSeriesStatement represents a command for listing series in the database.
type ShowSeriesStatement struct {
// Database to query. If blank, use the default database.
// The database can also be specified per source in the Sources.
Database string
// Measurement(s) the series are listed for.
Sources Sources
@ -2267,6 +2271,10 @@ func (s *ShowSeriesStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("SHOW SERIES")
if s.Database != "" {
_, _ = buf.WriteString(" ON ")
_, _ = buf.WriteString(QuoteIdent(s.Database))
}
if s.Sources != nil {
_, _ = buf.WriteString(" FROM ")
_, _ = buf.WriteString(s.Sources.String())
@ -2518,6 +2526,9 @@ func (s *DropContinuousQueryStatement) RequiredPrivileges() (ExecutionPrivileges
// ShowMeasurementsStatement represents a command for listing measurements.
type ShowMeasurementsStatement struct {
// Database to query. If blank, use the default database.
Database string
// Measurement name or regex.
Source Source
@ -2540,6 +2551,10 @@ func (s *ShowMeasurementsStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("SHOW MEASUREMENTS")
if s.Database != "" {
_, _ = buf.WriteString(" ON ")
_, _ = buf.WriteString(s.Database)
}
if s.Source != nil {
_, _ = buf.WriteString(" WITH MEASUREMENT ")
if m, ok := s.Source.(*Measurement); ok && m.Regex != nil {
@ -2614,8 +2629,11 @@ type ShowRetentionPoliciesStatement struct {
// String returns a string representation of a ShowRetentionPoliciesStatement.
func (s *ShowRetentionPoliciesStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("SHOW RETENTION POLICIES ON ")
_, _ = buf.WriteString(QuoteIdent(s.Database))
_, _ = buf.WriteString("SHOW RETENTION POLICIES")
if s.Database != "" {
_, _ = buf.WriteString(" ON ")
_, _ = buf.WriteString(QuoteIdent(s.Database))
}
return buf.String()
}
@ -2759,6 +2777,10 @@ func (s *ShowSubscriptionsStatement) RequiredPrivileges() (ExecutionPrivileges,
// ShowTagKeysStatement represents a command for listing tag keys.
type ShowTagKeysStatement struct {
// Database to query. If blank, use the default database.
// The database can also be specified per source in the Sources.
Database string
// Data sources that fields are extracted from.
Sources Sources
@ -2786,6 +2808,10 @@ func (s *ShowTagKeysStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("SHOW TAG KEYS")
if s.Database != "" {
_, _ = buf.WriteString(" ON ")
_, _ = buf.WriteString(QuoteIdent(s.Database))
}
if s.Sources != nil {
_, _ = buf.WriteString(" FROM ")
_, _ = buf.WriteString(s.Sources.String())
@ -2824,6 +2850,10 @@ func (s *ShowTagKeysStatement) RequiredPrivileges() (ExecutionPrivileges, error)
// ShowTagValuesStatement represents a command for listing tag values.
type ShowTagValuesStatement struct {
// Database to query. If blank, use the default database.
// The database can also be specified per source in the Sources.
Database string
// Data source that fields are extracted from.
Sources Sources
@ -2852,6 +2882,10 @@ func (s *ShowTagValuesStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("SHOW TAG VALUES")
if s.Database != "" {
_, _ = buf.WriteString(" ON ")
_, _ = buf.WriteString(QuoteIdent(s.Database))
}
if s.Sources != nil {
_, _ = buf.WriteString(" FROM ")
_, _ = buf.WriteString(s.Sources.String())
@ -2903,6 +2937,10 @@ func (s *ShowUsersStatement) RequiredPrivileges() (ExecutionPrivileges, error) {
// ShowFieldKeysStatement represents a command for listing field keys.
type ShowFieldKeysStatement struct {
// Database to query. If blank, use the default database.
// The database can also be specified per source in the Sources.
Database string
// Data sources that fields are extracted from.
Sources Sources
@ -2922,6 +2960,10 @@ func (s *ShowFieldKeysStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("SHOW FIELD KEYS")
if s.Database != "" {
_, _ = buf.WriteString(" ON ")
_, _ = buf.WriteString(QuoteIdent(s.Database))
}
if s.Sources != nil {
_, _ = buf.WriteString(" FROM ")
_, _ = buf.WriteString(s.Sources.String())

View File

@ -1045,6 +1045,17 @@ func (p *Parser) parseShowSeriesStatement() (*ShowSeriesStatement, error) {
stmt := &ShowSeriesStatement{}
var err error
// Parse optional ON clause.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == ON {
// Parse the database.
stmt.Database, err = p.parseIdent()
if err != nil {
return nil, err
}
} else {
p.unscan()
}
// Parse optional FROM.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == FROM {
if stmt.Sources, err = p.parseSources(); err != nil {
@ -1083,6 +1094,17 @@ func (p *Parser) parseShowMeasurementsStatement() (*ShowMeasurementsStatement, e
stmt := &ShowMeasurementsStatement{}
var err error
// Parse optional ON clause.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == ON {
// Parse the database.
stmt.Database, err = p.parseIdent()
if err != nil {
return nil, err
}
} else {
p.unscan()
}
// Parse optional WITH clause.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == WITH {
// Parse required MEASUREMENT token.
@ -1141,17 +1163,17 @@ func (p *Parser) parseShowRetentionPoliciesStatement() (*ShowRetentionPoliciesSt
stmt := &ShowRetentionPoliciesStatement{}
// Expect an "ON" keyword.
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != ON {
return nil, newParseError(tokstr(tok, lit), []string{"ON"}, pos)
if tok, _, _ := p.scanIgnoreWhitespace(); tok == ON {
// Parse the database.
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
stmt.Database = ident
} else {
p.unscan()
}
// Parse the database.
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
stmt.Database = ident
return stmt, nil
}
@ -1161,6 +1183,17 @@ func (p *Parser) parseShowTagKeysStatement() (*ShowTagKeysStatement, error) {
stmt := &ShowTagKeysStatement{}
var err error
// Parse optional ON clause.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == ON {
// Parse the database.
stmt.Database, err = p.parseIdent()
if err != nil {
return nil, err
}
} else {
p.unscan()
}
// Parse optional source.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == FROM {
if stmt.Sources, err = p.parseSources(); err != nil {
@ -1209,6 +1242,17 @@ func (p *Parser) parseShowTagValuesStatement() (*ShowTagValuesStatement, error)
stmt := &ShowTagValuesStatement{}
var err error
// Parse optional ON clause.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == ON {
// Parse the database.
stmt.Database, err = p.parseIdent()
if err != nil {
return nil, err
}
} else {
p.unscan()
}
// Parse optional source.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == FROM {
if stmt.Sources, err = p.parseSources(); err != nil {
@ -1314,6 +1358,17 @@ func (p *Parser) parseShowFieldKeysStatement() (*ShowFieldKeysStatement, error)
stmt := &ShowFieldKeysStatement{}
var err error
// Parse optional ON clause.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == ON {
// Parse the database.
stmt.Database, err = p.parseIdent()
if err != nil {
return nil, err
}
} else {
p.unscan()
}
// Parse optional source.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == FROM {
if stmt.Sources, err = p.parseSources(); err != nil {

View File

@ -1035,6 +1035,14 @@ func TestParser_ParseStatement(t *testing.T) {
},
},
// SHOW SERIES ON db0
{
s: `SHOW SERIES ON db0`,
stmt: &influxql.ShowSeriesStatement{
Database: "db0",
},
},
// SHOW SERIES FROM /<regex>/
{
s: `SHOW SERIES FROM /[cg]pu/`,
@ -1097,6 +1105,14 @@ func TestParser_ParseStatement(t *testing.T) {
},
},
// SHOW MEASUREMENTS ON db0
{
s: `SHOW MEASUREMENTS ON db0`,
stmt: &influxql.ShowMeasurementsStatement{
Database: "db0",
},
},
// SHOW MEASUREMENTS WITH MEASUREMENT = cpu
{
s: `SHOW MEASUREMENTS WITH MEASUREMENT = cpu`,
@ -1140,9 +1156,15 @@ func TestParser_ParseStatement(t *testing.T) {
// SHOW RETENTION POLICIES
{
s: `SHOW RETENTION POLICIES ON mydb`,
s: `SHOW RETENTION POLICIES`,
stmt: &influxql.ShowRetentionPoliciesStatement{},
},
// SHOW RETENTION POLICIES ON db0
{
s: `SHOW RETENTION POLICIES ON db0`,
stmt: &influxql.ShowRetentionPoliciesStatement{
Database: "mydb",
Database: "db0",
},
},
@ -1154,6 +1176,14 @@ func TestParser_ParseStatement(t *testing.T) {
},
},
// SHOW TAG KEYS ON db0
{
s: `SHOW TAG KEYS ON db0`,
stmt: &influxql.ShowTagKeysStatement{
Database: "db0",
},
},
// SHOW TAG KEYS with LIMIT
{
s: `SHOW TAG KEYS FROM src LIMIT 2`,
@ -1357,6 +1387,16 @@ func TestParser_ParseStatement(t *testing.T) {
},
},
// SHOW TAG VALUES ON db0
{
s: `SHOW TAG VALUES ON db0 WITH KEY = "host"`,
stmt: &influxql.ShowTagValuesStatement{
Database: "db0",
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
},
},
// SHOW USERS
{
s: `SHOW USERS`,
@ -1387,6 +1427,12 @@ func TestParser_ParseStatement(t *testing.T) {
},
},
},
{
s: `SHOW FIELD KEYS ON db0`,
stmt: &influxql.ShowFieldKeysStatement{
Database: "db0",
},
},
// DELETE statement
{
@ -2168,8 +2214,6 @@ func TestParser_ParseStatement(t *testing.T) {
{s: `SHOW CONTINUOUS`, err: `found EOF, expected QUERIES at line 1, char 17`},
{s: `SHOW RETENTION`, err: `found EOF, expected POLICIES at line 1, char 16`},
{s: `SHOW RETENTION ON`, err: `found ON, expected POLICIES at line 1, char 16`},
{s: `SHOW RETENTION POLICIES`, err: `found EOF, expected ON at line 1, char 25`},
{s: `SHOW RETENTION POLICIES mydb`, err: `found mydb, expected ON at line 1, char 25`},
{s: `SHOW RETENTION POLICIES ON`, err: `found EOF, expected identifier at line 1, char 28`},
{s: `SHOW SHARD`, err: `found EOF, expected GROUPS at line 1, char 12`},
{s: `SHOW FOO`, err: `found FOO, expected CONTINUOUS, DATABASES, DIAGNOSTICS, FIELD, GRANTS, MEASUREMENTS, QUERIES, RETENTION, SERIES, SHARD, SHARDS, STATS, SUBSCRIPTIONS, TAG, USERS at line 1, char 6`},

View File

@ -26,7 +26,7 @@ func rewriteShowFieldKeysStatement(stmt *ShowFieldKeysStatement) (Statement, err
{Expr: &VarRef{Val: "fieldKey"}},
{Expr: &VarRef{Val: "fieldType"}},
}),
Sources: rewriteSources(stmt.Sources, "_fieldKeys"),
Sources: rewriteSources(stmt.Sources, "_fieldKeys", stmt.Database),
Condition: rewriteSourcesCondition(stmt.Sources, nil),
Offset: stmt.Offset,
Limit: stmt.Limit,
@ -47,6 +47,7 @@ func rewriteShowMeasurementsStatement(stmt *ShowMeasurementsStatement) (Statemen
condition = rewriteSourcesCondition(Sources([]Source{stmt.Source}), stmt.Condition)
}
return &ShowMeasurementsStatement{
Database: stmt.Database,
Condition: condition,
Limit: stmt.Limit,
Offset: stmt.Offset,
@ -64,7 +65,7 @@ func rewriteShowSeriesStatement(stmt *ShowSeriesStatement) (Statement, error) {
Fields: []*Field{
{Expr: &VarRef{Val: "key"}},
},
Sources: rewriteSources(stmt.Sources, "_series"),
Sources: rewriteSources(stmt.Sources, "_series", stmt.Database),
Condition: rewriteSourcesCondition(stmt.Sources, stmt.Condition),
Offset: stmt.Offset,
Limit: stmt.Limit,
@ -140,7 +141,7 @@ func rewriteShowTagKeysStatement(stmt *ShowTagKeysStatement) (Statement, error)
Fields: []*Field{
{Expr: &VarRef{Val: "tagKey"}},
},
Sources: rewriteSources(stmt.Sources, "_tagKeys"),
Sources: rewriteSources(stmt.Sources, "_tagKeys", stmt.Database),
Condition: rewriteSourcesCondition(stmt.Sources, stmt.Condition),
Offset: stmt.Offset,
Limit: stmt.Limit,
@ -151,22 +152,29 @@ func rewriteShowTagKeysStatement(stmt *ShowTagKeysStatement) (Statement, error)
}
// rewriteSources rewrites sources with previous database and retention policy
func rewriteSources(sources Sources, measurementName string) Sources {
func rewriteSources(sources Sources, measurementName, defaultDatabase string) Sources {
newSources := Sources{}
for _, src := range sources {
if src == nil {
continue
}
mm := src.(*Measurement)
database := mm.Database
if database == "" {
database = defaultDatabase
}
newSources = append(newSources,
&Measurement{
Database: mm.Database,
Database: database,
RetentionPolicy: mm.RetentionPolicy,
Name: measurementName,
})
}
if len(newSources) <= 0 {
return append(newSources, &Measurement{Name: measurementName})
return append(newSources, &Measurement{
Database: defaultDatabase,
Name: measurementName,
})
}
return newSources
}

View File

@ -15,66 +15,130 @@ func TestRewriteStatement(t *testing.T) {
stmt: `SHOW FIELD KEYS`,
s: `SELECT fieldKey, fieldType FROM _fieldKeys`,
},
{
stmt: `SHOW FIELD KEYS ON db0`,
s: `SELECT fieldKey, fieldType FROM db0.._fieldKeys`,
},
{
stmt: `SHOW FIELD KEYS FROM cpu`,
s: `SELECT fieldKey, fieldType FROM _fieldKeys WHERE _name = 'cpu'`,
},
{
stmt: `SHOW FIELD KEYS ON db0 FROM cpu`,
s: `SELECT fieldKey, fieldType FROM db0.._fieldKeys WHERE _name = 'cpu'`,
},
{
stmt: `SHOW FIELD KEYS FROM /c.*/`,
s: `SELECT fieldKey, fieldType FROM _fieldKeys WHERE _name =~ /c.*/`,
},
{
stmt: `SHOW FIELD KEYS ON db0 FROM /c.*/`,
s: `SELECT fieldKey, fieldType FROM db0.._fieldKeys WHERE _name =~ /c.*/`,
},
{
stmt: `SHOW FIELD KEYS FROM mydb.myrp2.cpu`,
s: `SELECT fieldKey, fieldType FROM mydb.myrp2._fieldKeys WHERE _name = 'cpu'`,
},
{
stmt: `SHOW FIELD KEYS ON db0 FROM mydb.myrp2.cpu`,
s: `SELECT fieldKey, fieldType FROM mydb.myrp2._fieldKeys WHERE _name = 'cpu'`,
},
{
stmt: `SHOW FIELD KEYS FROM mydb.myrp2./c.*/`,
s: `SELECT fieldKey, fieldType FROM mydb.myrp2._fieldKeys WHERE _name =~ /c.*/`,
},
{
stmt: `SHOW FIELD KEYS ON db0 FROM mydb.myrp2./c.*/`,
s: `SELECT fieldKey, fieldType FROM mydb.myrp2._fieldKeys WHERE _name =~ /c.*/`,
},
{
stmt: `SHOW SERIES`,
s: `SELECT "key" FROM _series`,
},
{
stmt: `SHOW SERIES ON db0`,
s: `SELECT "key" FROM db0.._series`,
},
{
stmt: `SHOW SERIES FROM cpu`,
s: `SELECT "key" FROM _series WHERE _name = 'cpu'`,
},
{
stmt: `SHOW SERIES ON db0 FROM cpu`,
s: `SELECT "key" FROM db0.._series WHERE _name = 'cpu'`,
},
{
stmt: `SHOW SERIES FROM mydb.myrp1.cpu`,
s: `SELECT "key" FROM mydb.myrp1._series WHERE _name = 'cpu'`,
},
{
stmt: `SHOW SERIES ON db0 FROM mydb.myrp1.cpu`,
s: `SELECT "key" FROM mydb.myrp1._series WHERE _name = 'cpu'`,
},
{
stmt: `SHOW SERIES FROM mydb.myrp1./c.*/`,
s: `SELECT "key" FROM mydb.myrp1._series WHERE _name =~ /c.*/`,
},
{
stmt: `SHOW SERIES ON db0 FROM mydb.myrp1./c.*/`,
s: `SELECT "key" FROM mydb.myrp1._series WHERE _name =~ /c.*/`,
},
{
stmt: `SHOW TAG KEYS`,
s: `SELECT tagKey FROM _tagKeys`,
},
{
stmt: `SHOW TAG KEYS ON db0`,
s: `SELECT tagKey FROM db0.._tagKeys`,
},
{
stmt: `SHOW TAG KEYS FROM cpu`,
s: `SELECT tagKey FROM _tagKeys WHERE _name = 'cpu'`,
},
{
stmt: `SHOW TAG KEYS ON db0 FROM cpu`,
s: `SELECT tagKey FROM db0.._tagKeys WHERE _name = 'cpu'`,
},
{
stmt: `SHOW TAG KEYS FROM /c.*/`,
s: `SELECT tagKey FROM _tagKeys WHERE _name =~ /c.*/`,
},
{
stmt: `SHOW TAG KEYS ON db0 FROM /c.*/`,
s: `SELECT tagKey FROM db0.._tagKeys WHERE _name =~ /c.*/`,
},
{
stmt: `SHOW TAG KEYS FROM cpu WHERE region = 'uswest'`,
s: `SELECT tagKey FROM _tagKeys WHERE (_name = 'cpu') AND (region = 'uswest')`,
},
{
stmt: `SHOW TAG KEYS ON db0 FROM cpu WHERE region = 'uswest'`,
s: `SELECT tagKey FROM db0.._tagKeys WHERE (_name = 'cpu') AND (region = 'uswest')`,
},
{
stmt: `SHOW TAG KEYS FROM mydb.myrp1.cpu`,
s: `SELECT tagKey FROM mydb.myrp1._tagKeys WHERE _name = 'cpu'`,
},
{
stmt: `SHOW TAG KEYS ON db0 FROM mydb.myrp1.cpu`,
s: `SELECT tagKey FROM mydb.myrp1._tagKeys WHERE _name = 'cpu'`,
},
{
stmt: `SHOW TAG KEYS FROM mydb.myrp1./c.*/`,
s: `SELECT tagKey FROM mydb.myrp1._tagKeys WHERE _name =~ /c.*/`,
},
{
stmt: `SHOW TAG KEYS ON db0 FROM mydb.myrp1./c.*/`,
s: `SELECT tagKey FROM mydb.myrp1._tagKeys WHERE _name =~ /c.*/`,
},
{
stmt: `SHOW TAG KEYS FROM mydb.myrp1.cpu WHERE region = 'uswest'`,
s: `SELECT tagKey FROM mydb.myrp1._tagKeys WHERE (_name = 'cpu') AND (region = 'uswest')`,
},
{
stmt: `SHOW TAG KEYS ON db0 FROM mydb.myrp1.cpu WHERE region = 'uswest'`,
s: `SELECT tagKey FROM mydb.myrp1._tagKeys WHERE (_name = 'cpu') AND (region = 'uswest')`,
},
{
stmt: `SELECT value FROM cpu`,
s: `SELECT value FROM cpu`,