Add all versions of CARDINALITY commands

pull/8984/head
Edd Robinson 2017-10-23 18:54:21 +01:00
parent 50d73497c5
commit bda6de9817
6 changed files with 599 additions and 57 deletions

View File

@ -2102,7 +2102,7 @@ type ShowSeriesCardinalityStatement struct {
// The database can also be specified per source in the Sources.
Database string
// If exact is false then the cardinality estimation approach is used.
// Specifies whether the user requires exact counting or not.
Exact bool
// Measurement(s) the series are listed for.
@ -2734,6 +2734,7 @@ func (s *ShowTagKeysStatement) DefaultDatabase() string {
// ShowTagKeyCardinalityStatement represents a command for listing tag key cardinality.
type ShowTagKeyCardinalityStatement struct {
Database string
Exact bool
Sources Sources
Condition Expr
Dimensions Dimensions
@ -2743,7 +2744,11 @@ type ShowTagKeyCardinalityStatement struct {
// String returns a string representation of the statement.
func (s *ShowTagKeyCardinalityStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("SHOW TAG KEY EXACT CARDINALITY")
_, _ = buf.WriteString("SHOW TAG KEY ")
if s.Exact {
_, _ = buf.WriteString("EXACT ")
}
_, _ = buf.WriteString("CARDINALITY")
if s.Database != "" {
_, _ = buf.WriteString(" ON ")
@ -2863,6 +2868,7 @@ func (s *ShowTagValuesStatement) DefaultDatabase() string {
// ShowTagValuesCardinalityStatement represents a command for listing tag value cardinality.
type ShowTagValuesCardinalityStatement struct {
Database string
Exact bool
Sources Sources
Op Token
TagKeyExpr Literal
@ -2874,7 +2880,11 @@ type ShowTagValuesCardinalityStatement struct {
// String returns a string representation of the statement.
func (s *ShowTagValuesCardinalityStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("SHOW TAG VALUES EXACT CARDINALITY")
_, _ = buf.WriteString("SHOW TAG VALUES ")
if s.Exact {
_, _ = buf.WriteString("EXACT ")
}
_, _ = buf.WriteString("CARDINALITY")
if s.Database != "" {
_, _ = buf.WriteString(" ON ")
@ -2936,6 +2946,7 @@ func (s *ShowUsersStatement) RequiredPrivileges() (ExecutionPrivileges, error) {
// ShowFieldKeyCardinalityStatement represents a command for listing field key cardinality.
type ShowFieldKeyCardinalityStatement struct {
Database string
Exact bool
Sources Sources
Condition Expr
Dimensions Dimensions
@ -2945,7 +2956,12 @@ type ShowFieldKeyCardinalityStatement struct {
// String returns a string representation of the statement.
func (s *ShowFieldKeyCardinalityStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("SHOW FIELD KEY EXACT CARDINALITY")
_, _ = buf.WriteString("SHOW FIELD KEY ")
if s.Exact {
_, _ = buf.WriteString("EXACT ")
}
_, _ = buf.WriteString("CARDINALITY")
if s.Database != "" {
_, _ = buf.WriteString(" ON ")

View File

@ -118,7 +118,7 @@ func init() {
})
show.Group(FIELD).With(func(field *ParseTree) {
field.Handle(KEY, func(p *Parser) (Statement, error) {
return p.parseShowFieldKeyStatement()
return p.parseShowFieldKeyCardinalityStatement()
})
field.Handle(KEYS, func(p *Parser) (Statement, error) {
return p.parseShowFieldKeysStatement()

View File

@ -974,11 +974,6 @@ func (p *Parser) parseShowSeriesCardinalityStatement(exact bool) (Statement, err
p.Unscan()
}
// Estimation command doesn't support any further versions of the command.
if !exact {
return stmt, nil
}
// Parse optional FROM.
if tok, _, _ := p.ScanIgnoreWhitespace(); tok == FROM {
if stmt.Sources, err = p.parseSources(false); err != nil {
@ -1008,8 +1003,7 @@ func (p *Parser) parseShowSeriesCardinalityStatement(exact bool) (Statement, err
return stmt, nil
}
// This function assumes the "SHOW MEASUREMENT EXACT" or "SHOW MEASUREMENT CARDINALITY"
// tokens have already been consumed.
// This function assumes the "SHOW MEASUREMENT" tokens have already been consumed.
func (p *Parser) parseShowMeasurementCardinalityStatement(exact bool) (Statement, error) {
stmt := &ShowMeasurementCardinalityStatement{Exact: exact}
@ -1030,11 +1024,6 @@ func (p *Parser) parseShowMeasurementCardinalityStatement(exact bool) (Statement
p.Unscan()
}
// Estimation command doesn't support any further versions of the command.
if !stmt.Exact {
return stmt, nil
}
// Parse optional FROM.
if tok, _, _ := p.ScanIgnoreWhitespace(); tok == FROM {
if stmt.Sources, err = p.parseSources(false); err != nil {
@ -1156,15 +1145,20 @@ func (p *Parser) parseShowRetentionPoliciesStatement() (*ShowRetentionPoliciesSt
// This function assumes the "SHOW TAG KEY" tokens have already been consumed.
func (p *Parser) parseShowTagKeyCardinalityStatement() (Statement, error) {
var err error
stmt := &ShowTagKeyCardinalityStatement{}
if tok, pos, lit := p.ScanIgnoreWhitespace(); tok != EXACT {
return nil, newParseError(tokstr(tok, lit), []string{"EXACT"}, pos)
var exactCardinality bool
requiredTokens := []string{"EXACT", "CARDINALITY"}
if tok, _, _ := p.ScanIgnoreWhitespace(); tok == EXACT {
exactCardinality = true
requiredTokens = requiredTokens[1:]
} else {
p.Unscan()
}
stmt := &ShowTagKeyCardinalityStatement{Exact: exactCardinality}
// Parse remaining CARDINALITY token
if tok, pos, lit := p.ScanIgnoreWhitespace(); tok != CARDINALITY {
return nil, newParseError(tokstr(tok, lit), []string{"CARDINALITY"}, pos)
return nil, newParseError(tokstr(tok, lit), requiredTokens, pos)
}
// Parse optional ON clause.
@ -1271,7 +1265,9 @@ func (p *Parser) parseShowTagValuesStatement() (Statement, error) {
var err error
if tok, _, _ := p.ScanIgnoreWhitespace(); tok == EXACT {
return p.parseShowTagValuesCardinalityStatement()
return p.parseShowTagValuesCardinalityStatement(true)
} else if tok == CARDINALITY {
return p.parseShowTagValuesCardinalityStatement(false)
}
p.Unscan()
@ -1323,14 +1319,16 @@ func (p *Parser) parseShowTagValuesStatement() (Statement, error) {
return stmt, nil
}
// This function assumes the "SHOW TAG VALUES EXACT" tokens have already been consumed.
func (p *Parser) parseShowTagValuesCardinalityStatement() (Statement, error) {
// This function assumes the "SHOW TAG VALUES" tokens have already been consumed.
func (p *Parser) parseShowTagValuesCardinalityStatement(exact bool) (Statement, error) {
var err error
stmt := &ShowTagValuesCardinalityStatement{}
stmt := &ShowTagValuesCardinalityStatement{Exact: exact}
// Parse remaining CARDINALITY token
if tok, pos, lit := p.ScanIgnoreWhitespace(); tok != CARDINALITY {
return nil, newParseError(tokstr(tok, lit), []string{"CARDINALITY"}, pos)
if stmt.Exact {
// Parse remaining CARDINALITY token
if tok, pos, lit := p.ScanIgnoreWhitespace(); tok != CARDINALITY {
return nil, newParseError(tokstr(tok, lit), []string{"CARDINALITY"}, pos)
}
}
// Parse optional ON clause.
@ -1376,7 +1374,7 @@ func (p *Parser) parseShowTagValuesCardinalityStatement() (Statement, error) {
return stmt, nil
}
// parseTagKeys parses a string and returns a list of tag keys.
// parseTagKeys pa³rses a string and returns a list of tag keys.
func (p *Parser) parseTagKeyExpr() (Token, Literal, error) {
var err error
@ -1438,23 +1436,23 @@ func (p *Parser) parseShowSubscriptionsStatement() (*ShowSubscriptionsStatement,
return stmt, nil
}
// parseShowFieldKeyStatement parses a string and returns a Statement.
// This function assumes the "SHOW FIELD KEY" tokens have already been consumed.
func (p *Parser) parseShowFieldKeyStatement() (Statement, error) {
if tok, pos, lit := p.ScanIgnoreWhitespace(); tok != EXACT {
return nil, newParseError(tokstr(tok, lit), []string{"EXACT"}, pos)
}
return p.parseShowFieldKeyCardinalityStatement()
}
// This function assumes the "SHOW FIELD KEY EXACT" tokens have already been consumed.
func (p *Parser) parseShowFieldKeyCardinalityStatement() (Statement, error) {
var err error
stmt := &ShowFieldKeyCardinalityStatement{}
var exactCardinality bool
requiredTokens := []string{"EXACT", "CARDINALITY"}
if tok, _, _ := p.ScanIgnoreWhitespace(); tok == EXACT {
exactCardinality = true
requiredTokens = requiredTokens[1:]
} else {
p.Unscan()
}
stmt := &ShowFieldKeyCardinalityStatement{Exact: exactCardinality}
// Parse remaining CARDINALITY token
if tok, pos, lit := p.ScanIgnoreWhitespace(); tok != CARDINALITY {
return nil, newParseError(tokstr(tok, lit), []string{"CARDINALITY"}, pos)
return nil, newParseError(tokstr(tok, lit), requiredTokens, pos)
}
// Parse optional ON clause.

View File

@ -1802,17 +1802,78 @@ func TestParser_ParseStatement(t *testing.T) {
Database: "db0",
},
},
// SHOW TAG KEY CARDINALITY statement
{
s: `SHOW TAG KEY CARDINALITY`,
stmt: &influxql.ShowTagKeyCardinalityStatement{},
},
// SHOW TAG KEY CARDINALITY FROM cpu
{
s: `SHOW TAG KEY CARDINALITY FROM cpu`,
stmt: &influxql.ShowTagKeyCardinalityStatement{
Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}},
},
},
// SHOW TAG KEY CARDINALITY ON db0
{
s: `SHOW TAG KEY CARDINALITY ON db0`,
stmt: &influxql.ShowTagKeyCardinalityStatement{
Database: "db0",
},
},
// SHOW TAG KEY CARDINALITY FROM /<regex>/
{
s: `SHOW TAG KEY CARDINALITY FROM /[cg]pu/`,
stmt: &influxql.ShowTagKeyCardinalityStatement{
Sources: []influxql.Source{
&influxql.Measurement{
Regex: &influxql.RegexLiteral{Val: regexp.MustCompile(`[cg]pu`)},
},
},
},
},
// SHOW TAG KEY CARDINALITY with OFFSET 0
{
s: `SHOW TAG KEY CARDINALITY OFFSET 0`,
stmt: &influxql.ShowTagKeyCardinalityStatement{Offset: 0},
},
// SHOW TAG KEY CARDINALITY with LIMIT 2 OFFSET 0
{
s: `SHOW TAG KEY CARDINALITY LIMIT 2 OFFSET 0`,
stmt: &influxql.ShowTagKeyCardinalityStatement{Offset: 0, Limit: 2},
},
// SHOW TAG KEY CARDINALITY WHERE with ORDER BY and LIMIT
{
s: `SHOW TAG KEY CARDINALITY WHERE region = 'order by desc' LIMIT 10`,
stmt: &influxql.ShowTagKeyCardinalityStatement{
Condition: &influxql.BinaryExpr{
Op: influxql.EQ,
LHS: &influxql.VarRef{Val: "region"},
RHS: &influxql.StringLiteral{Val: "order by desc"},
},
Limit: 10,
},
},
// SHOW TAG KEY EXACT CARDINALITY statement
{
s: `SHOW TAG KEY EXACT CARDINALITY`,
stmt: &influxql.ShowTagKeyCardinalityStatement{},
s: `SHOW TAG KEY EXACT CARDINALITY`,
stmt: &influxql.ShowTagKeyCardinalityStatement{
Exact: true,
},
},
// SHOW TAG KEY EXACT CARDINALITY FROM cpu
{
s: `SHOW TAG KEY EXACT CARDINALITY FROM cpu`,
stmt: &influxql.ShowTagKeyCardinalityStatement{
Exact: true,
Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}},
},
},
@ -1821,6 +1882,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW TAG KEY EXACT CARDINALITY ON db0`,
stmt: &influxql.ShowTagKeyCardinalityStatement{
Exact: true,
Database: "db0",
},
},
@ -1829,6 +1891,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW TAG KEY EXACT CARDINALITY FROM /[cg]pu/`,
stmt: &influxql.ShowTagKeyCardinalityStatement{
Exact: true,
Sources: []influxql.Source{
&influxql.Measurement{
Regex: &influxql.RegexLiteral{Val: regexp.MustCompile(`[cg]pu`)},
@ -1840,19 +1903,20 @@ func TestParser_ParseStatement(t *testing.T) {
// SHOW TAG KEY EXACT CARDINALITY with OFFSET 0
{
s: `SHOW TAG KEY EXACT CARDINALITY OFFSET 0`,
stmt: &influxql.ShowTagKeyCardinalityStatement{Offset: 0},
stmt: &influxql.ShowTagKeyCardinalityStatement{Exact: true, Offset: 0},
},
// SHOW TAG KEY EXACT CARDINALITY with LIMIT 2 OFFSET 0
{
s: `SHOW TAG KEY EXACT CARDINALITY LIMIT 2 OFFSET 0`,
stmt: &influxql.ShowTagKeyCardinalityStatement{Offset: 0, Limit: 2},
stmt: &influxql.ShowTagKeyCardinalityStatement{Exact: true, Offset: 0, Limit: 2},
},
// SHOW TAG KEY EXACT CARDINALITY WHERE with ORDER BY and LIMIT
{
s: `SHOW TAG KEY EXACT CARDINALITY WHERE region = 'order by desc' LIMIT 10`,
stmt: &influxql.ShowTagKeyCardinalityStatement{
Exact: true,
Condition: &influxql.BinaryExpr{
Op: influxql.EQ,
LHS: &influxql.VarRef{Val: "region"},
@ -2091,10 +2155,90 @@ func TestParser_ParseStatement(t *testing.T) {
},
},
// SHOW TAG VALUES CARDINALITY statement
{
s: `SHOW TAG VALUES CARDINALITY WITH KEY = host`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
},
},
// SHOW TAG VALUES CARDINALITY FROM cpu
{
s: `SHOW TAG VALUES CARDINALITY FROM cpu WITH KEY = host`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}},
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
},
},
// SHOW TAG VALUES CARDINALITY ON db0
{
s: `SHOW TAG VALUES CARDINALITY ON db0 WITH KEY = host`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Database: "db0",
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
},
},
// SHOW TAG VALUES CARDINALITY FROM /<regex>/
{
s: `SHOW TAG VALUES CARDINALITY FROM /[cg]pu/ WITH KEY = host`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Sources: []influxql.Source{
&influxql.Measurement{
Regex: &influxql.RegexLiteral{Val: regexp.MustCompile(`[cg]pu`)},
},
},
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
},
},
// SHOW TAG VALUES CARDINALITY with OFFSET 0
{
s: `SHOW TAG VALUES CARDINALITY WITH KEY = host OFFSET 0`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
Offset: 0,
},
},
// SHOW TAG VALUES CARDINALITY with LIMIT 2 OFFSET 0
{
s: `SHOW TAG VALUES CARDINALITY WITH KEY = host LIMIT 2 OFFSET 0`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
Offset: 0,
Limit: 2,
},
},
// SHOW TAG VALUES CARDINALITY WHERE with ORDER BY and LIMIT
{
s: `SHOW TAG VALUES CARDINALITY WITH KEY = host WHERE region = 'order by desc' LIMIT 10`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
Condition: &influxql.BinaryExpr{
Op: influxql.EQ,
LHS: &influxql.VarRef{Val: "region"},
RHS: &influxql.StringLiteral{Val: "order by desc"},
},
Limit: 10,
},
},
// SHOW TAG VALUES EXACT CARDINALITY statement
{
s: `SHOW TAG VALUES EXACT CARDINALITY WITH KEY = host`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Exact: true,
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
},
@ -2104,6 +2248,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW TAG VALUES EXACT CARDINALITY FROM cpu WITH KEY = host`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Exact: true,
Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}},
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
@ -2114,6 +2259,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW TAG VALUES EXACT CARDINALITY ON db0 WITH KEY = host`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Exact: true,
Database: "db0",
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
@ -2124,6 +2270,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW TAG VALUES EXACT CARDINALITY FROM /[cg]pu/ WITH KEY = host`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Exact: true,
Sources: []influxql.Source{
&influxql.Measurement{
Regex: &influxql.RegexLiteral{Val: regexp.MustCompile(`[cg]pu`)},
@ -2138,6 +2285,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW TAG VALUES EXACT CARDINALITY WITH KEY = host OFFSET 0`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Exact: true,
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
Offset: 0,
@ -2148,6 +2296,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW TAG VALUES EXACT CARDINALITY WITH KEY = host LIMIT 2 OFFSET 0`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Exact: true,
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
Offset: 0,
@ -2159,6 +2308,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW TAG VALUES EXACT CARDINALITY WITH KEY = host WHERE region = 'order by desc' LIMIT 10`,
stmt: &influxql.ShowTagValuesCardinalityStatement{
Exact: true,
Op: influxql.EQ,
TagKeyExpr: &influxql.StringLiteral{Val: "host"},
Condition: &influxql.BinaryExpr{
@ -2207,16 +2357,83 @@ func TestParser_ParseStatement(t *testing.T) {
},
},
// SHOW FIELD KEY CARDINALITY statement
{
s: `SHOW FIELD KEY CARDINALITY`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{},
},
// SHOW FIELD KEY CARDINALITY FROM cpu
{
s: `SHOW FIELD KEY CARDINALITY FROM cpu`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}},
},
},
// SHOW FIELD KEY CARDINALITY ON db0
{
s: `SHOW FIELD KEY CARDINALITY ON db0`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Database: "db0",
},
},
// SHOW FIELD KEY CARDINALITY FROM /<regex>/
{
s: `SHOW FIELD KEY CARDINALITY FROM /[cg]pu/`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Sources: []influxql.Source{
&influxql.Measurement{
Regex: &influxql.RegexLiteral{Val: regexp.MustCompile(`[cg]pu`)},
},
},
},
},
// SHOW FIELD KEY CARDINALITY with OFFSET 0
{
s: `SHOW FIELD KEY CARDINALITY OFFSET 0`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Offset: 0,
},
},
// SHOW FIELD KEY CARDINALITY with LIMIT 2 OFFSET 0
{
s: `SHOW FIELD KEY CARDINALITY LIMIT 2 OFFSET 0`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Offset: 0,
Limit: 2,
},
},
// SHOW FIELD KEY CARDINALITY WHERE with ORDER BY and LIMIT
{
s: `SHOW FIELD KEY CARDINALITY WHERE region = 'order by desc' LIMIT 10`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Condition: &influxql.BinaryExpr{
Op: influxql.EQ,
LHS: &influxql.VarRef{Val: "region"},
RHS: &influxql.StringLiteral{Val: "order by desc"},
},
Limit: 10,
},
},
// SHOW FIELD KEY EXACT CARDINALITY statement
{
s: `SHOW FIELD KEY EXACT CARDINALITY`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{},
s: `SHOW FIELD KEY EXACT CARDINALITY`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Exact: true,
},
},
// SHOW FIELD KEY EXACT CARDINALITY FROM cpu
{
s: `SHOW FIELD KEY EXACT CARDINALITY FROM cpu`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Exact: true,
Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}},
},
},
@ -2225,6 +2442,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW FIELD KEY EXACT CARDINALITY ON db0`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Exact: true,
Database: "db0",
},
},
@ -2233,6 +2451,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW FIELD KEY EXACT CARDINALITY FROM /[cg]pu/`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Exact: true,
Sources: []influxql.Source{
&influxql.Measurement{
Regex: &influxql.RegexLiteral{Val: regexp.MustCompile(`[cg]pu`)},
@ -2245,6 +2464,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW FIELD KEY EXACT CARDINALITY OFFSET 0`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Exact: true,
Offset: 0,
},
},
@ -2253,6 +2473,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW FIELD KEY EXACT CARDINALITY LIMIT 2 OFFSET 0`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Exact: true,
Offset: 0,
Limit: 2,
},
@ -2262,6 +2483,7 @@ func TestParser_ParseStatement(t *testing.T) {
{
s: `SHOW FIELD KEY EXACT CARDINALITY WHERE region = 'order by desc' LIMIT 10`,
stmt: &influxql.ShowFieldKeyCardinalityStatement{
Exact: true,
Condition: &influxql.BinaryExpr{
Op: influxql.EQ,
LHS: &influxql.VarRef{Val: "region"},
@ -2270,7 +2492,6 @@ func TestParser_ParseStatement(t *testing.T) {
Limit: 10,
},
},
// DELETE statement
{
s: `DELETE FROM src`,

View File

@ -111,7 +111,10 @@ func rewriteShowMeasurementsStatement(stmt *ShowMeasurementsStatement) (Statemen
}
func rewriteShowMeasurementCardinalityStatement(stmt *ShowMeasurementCardinalityStatement) (Statement, error) {
if !stmt.Exact { // Use cardinality estimation and don't rewrite.
// TODO(edd): currently we only support cardinality estimation for certain
// types of query. As the estimation coverage is expanded, this condition
// will become less strict.
if !stmt.Exact && stmt.Sources == nil && stmt.Condition == nil && stmt.Dimensions == nil && stmt.Limit == 0 && stmt.Offset == 0 {
return stmt, nil
}
@ -170,7 +173,10 @@ func rewriteShowSeriesStatement(stmt *ShowSeriesStatement) (Statement, error) {
}
func rewriteShowSeriesCardinalityStatement(stmt *ShowSeriesCardinalityStatement) (Statement, error) {
if !stmt.Exact {
// TODO(edd): currently we only support cardinality estimation for certain
// types of query. As the estimation coverage is expanded, this condition
// will become less strict.
if !stmt.Exact && stmt.Sources == nil && stmt.Condition == nil && stmt.Dimensions == nil && stmt.Limit == 0 && stmt.Offset == 0 {
return stmt, nil
}

View File

@ -1,6 +1,7 @@
package tests
import (
"encoding/json"
"flag"
"fmt"
"net/http"
@ -7217,6 +7218,76 @@ func TestServer_Query_ShowSeries(t *testing.T) {
}
}
func TestServer_Query_ShowSeriesCardinalityEstimation(t *testing.T) {
t.Parallel()
s := OpenServer(NewConfig())
defer s.Close()
if err := s.CreateDatabaseAndRetentionPolicy("db0", newRetentionPolicySpec("rp0", 1, 0), true); err != nil {
t.Fatal(err)
}
test := NewTest("db0", "rp0")
test.writes = make(Writes, 0, 10)
// Add 1,000,000 series.
for j := 0; j < cap(test.writes); j++ {
writes := make([]string, 0, 50000)
for i := 0; i < cap(writes); i++ {
writes = append(writes, fmt.Sprintf(`cpu,l=%d,h=s%d v=1 %d`, j, i, mustParseTime(time.RFC3339Nano, "2009-11-10T23:00:01Z").UnixNano()))
}
test.writes = append(test.writes, &Write{data: strings.Join(writes, "\n")})
}
// These queries use index sketches to estimate cardinality.
test.addQueries([]*Query{
&Query{
name: `show series cardinality`,
command: "SHOW SERIES CARDINALITY",
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show series cardinality on db0`,
command: "SHOW SERIES CARDINALITY ON db0",
},
}...)
for i, query := range test.queries {
t.Run(query.name, func(t *testing.T) {
if i == 0 {
if err := test.init(s); err != nil {
t.Fatalf("test init failed: %s", err)
}
}
if query.skip {
t.Skipf("SKIP:: %s", query.name)
}
if err := query.Execute(s); err != nil {
t.Error(query.Error(err))
}
// Manually parse result rather than comparing results string, as
// results are not deterministic.
got := struct {
Results []struct {
Series []struct {
Values [][]int
}
}
}{}
t.Log(query.act)
if err := json.Unmarshal([]byte(query.act), &got); err != nil {
t.Error(err)
}
cardinality := got.Results[0].Series[0].Values[0][0]
if cardinality < 450000 || cardinality > 550000 {
t.Errorf("got cardinality %d, which is 10%% or more away from expected estimation of 500,000", cardinality)
}
})
}
}
func TestServer_Query_ShowSeriesExactCardinality(t *testing.T) {
t.Parallel()
s := OpenServer(NewConfig())
@ -7242,6 +7313,48 @@ func TestServer_Query_ShowSeriesExactCardinality(t *testing.T) {
}
test.addQueries([]*Query{
&Query{
name: `show series cardinality from measurement`,
command: "SHOW SERIES CARDINALITY FROM cpu",
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[4]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show series cardinality from regular expression`,
command: "SHOW SERIES CARDINALITY FROM /[cg]pu/",
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[4]]},{"name":"gpu","columns":["count"],"values":[[2]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show series cardinality with where tag`,
command: "SHOW SERIES CARDINALITY WHERE region = 'uswest'",
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[1]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show series cardinality where tag matches regular expression`,
command: "SHOW SERIES CARDINALITY WHERE region =~ /ca.*/",
exp: `{"results":[{"statement_id":0,"series":[{"name":"disk","columns":["count"],"values":[[1]]},{"name":"gpu","columns":["count"],"values":[[1]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show series cardinality`,
command: "SHOW SERIES CARDINALITY WHERE host !~ /server0[12]/",
exp: `{"results":[{"statement_id":0,"series":[{"name":"disk","columns":["count"],"values":[[1]]},{"name":"gpu","columns":["count"],"values":[[1]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show series cardinality with from and where`,
command: "SHOW SERIES CARDINALITY FROM cpu WHERE region = 'useast'",
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[2]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show series cardinality with WHERE time should fail`,
command: "SHOW SERIES CARDINALITY WHERE time > now() - 1h",
exp: `{"results":[{"statement_id":0,"error":"SHOW SERIES EXACT CARDINALITY doesn't support time in WHERE clause"}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show series exact cardinality`,
command: "SHOW SERIES EXACT CARDINALITY",
@ -7434,7 +7547,81 @@ func TestServer_Query_ShowMeasurements(t *testing.T) {
}
}
func TestServer_Query_ShowMeasurementCardinality(t *testing.T) {
func TestServer_Query_ShowMeasurementCardinalityEstimation(t *testing.T) {
if testing.Short() {
t.Skip("skipping expensive test")
}
t.Parallel()
s := OpenServer(NewConfig())
defer s.Close()
if err := s.CreateDatabaseAndRetentionPolicy("db0", newRetentionPolicySpec("rp0", 1, 0), true); err != nil {
t.Fatal(err)
}
test := NewTest("db0", "rp0")
test.writes = make(Writes, 0, 10)
// Add 1,000,000 series.
for j := 0; j < cap(test.writes); j++ {
writes := make([]string, 0, 50000)
for i := 0; i < cap(writes); i++ {
writes = append(writes, fmt.Sprintf(`cpu-%d-s%d v=1 %d`, j, i, mustParseTime(time.RFC3339Nano, "2009-11-10T23:00:01Z").UnixNano()))
}
test.writes = append(test.writes, &Write{data: strings.Join(writes, "\n")})
}
// These queries use index sketches to estimate cardinality.
test.addQueries([]*Query{
&Query{
name: `show measurement cardinality`,
command: "SHOW MEASUREMENT CARDINALITY",
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show measurement cardinality on db0`,
command: "SHOW MEASUREMENT CARDINALITY ON db0",
},
}...)
for i, query := range test.queries {
t.Run(query.name, func(t *testing.T) {
if i == 0 {
if err := test.init(s); err != nil {
t.Fatalf("test init failed: %s", err)
}
}
if query.skip {
t.Skipf("SKIP:: %s", query.name)
}
if err := query.Execute(s); err != nil {
t.Error(query.Error(err))
}
// Manually parse result rather than comparing results string, as
// results are not deterministic.
got := struct {
Results []struct {
Series []struct {
Values [][]int
}
}
}{}
t.Log(query.act)
if err := json.Unmarshal([]byte(query.act), &got); err != nil {
t.Error(err)
}
cardinality := got.Results[0].Series[0].Values[0][0]
if cardinality < 450000 || cardinality > 550000 {
t.Errorf("got cardinality %d, which is 10%% or more away from expected estimation of 500,000", cardinality)
}
})
}
}
func TestServer_Query_ShowMeasurementExactCardinality(t *testing.T) {
t.Parallel()
s := OpenServer(NewConfig())
defer s.Close()
@ -7460,15 +7647,34 @@ func TestServer_Query_ShowMeasurementCardinality(t *testing.T) {
test.addQueries([]*Query{
&Query{
name: `show measurement cardinality`,
command: "SHOW MEASUREMENT CARDINALITY",
exp: `{"results":[{"statement_id":0,"series":[{"columns":["cardinality"],"values":[[3]]}]}]}`,
name: `show measurement cardinality using FROM and regex`,
command: "SHOW MEASUREMENT CARDINALITY FROM /[cg]pu/",
exp: `{"results":[{"statement_id":0,"series":[{"columns":["count"],"values":[[2]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show measurement cardinality on db0`,
command: "SHOW MEASUREMENT CARDINALITY ON db0",
exp: `{"results":[{"statement_id":0,"series":[{"columns":["cardinality"],"values":[[3]]}]}]}`,
name: `show measurement cardinality using FROM and regex - no matches`,
command: "SHOW MEASUREMENT CARDINALITY FROM /.*zzzzz.*/",
exp: `{"results":[{"statement_id":0}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show measurement cardinality where tag matches regular expression`,
command: "SHOW MEASUREMENT CARDINALITY WHERE region =~ /ca.*/",
exp: `{"results":[{"statement_id":0,"series":[{"columns":["count"],"values":[[2]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show measurement cardinality where tag does not match a regular expression`,
command: "SHOW MEASUREMENT CARDINALITY WHERE region !~ /ca.*/",
exp: `{"results":[{"statement_id":0,"series":[{"columns":["count"],"values":[[2]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show measurement cardinality with time in WHERE clauses errors`,
command: `SHOW MEASUREMENT CARDINALITY WHERE time > now() - 1h`,
exp: `{"results":[{"statement_id":0,"error":"SHOW MEASUREMENT EXACT CARDINALITY doesn't support time in WHERE clause"}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show measurement exact cardinality`,
@ -7700,6 +7906,41 @@ func TestServer_Query_ShowTagKeyCardinality(t *testing.T) {
}
test.addQueries([]*Query{
&Query{
name: `show tag key cardinality`,
command: "SHOW TAG KEY CARDINALITY",
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[2]]},{"name":"disk","columns":["count"],"values":[[2]]},{"name":"gpu","columns":["count"],"values":[[2]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show tag key cardinality on db0`,
command: "SHOW TAG KEY CARDINALITY ON db0",
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[2]]},{"name":"disk","columns":["count"],"values":[[2]]},{"name":"gpu","columns":["count"],"values":[[2]]}]}]}`,
},
&Query{
name: "show tag key cardinality from",
command: "SHOW TAG KEY CARDINALITY FROM cpu",
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[2]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: "show tag key cardinality from regex",
command: "SHOW TAG KEY CARDINALITY FROM /[cg]pu/",
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[2]]},{"name":"gpu","columns":["count"],"values":[[2]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: "show tag key cardinality measurement not found",
command: "SHOW TAG KEY CARDINALITY FROM doesntexist",
exp: `{"results":[{"statement_id":0}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: "show tag key cardinality with time in WHERE clause errors",
command: "SHOW TAG KEY CARDINALITY FROM cpu WHERE time > now() - 1h",
exp: `{"results":[{"statement_id":0,"error":"SHOW TAG KEY EXACT CARDINALITY doesn't support time in WHERE clause"}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show tag key exact cardinality`,
command: "SHOW TAG KEY EXACT CARDINALITY",
@ -7735,6 +7976,48 @@ func TestServer_Query_ShowTagKeyCardinality(t *testing.T) {
exp: `{"results":[{"statement_id":0,"error":"SHOW TAG KEY EXACT CARDINALITY doesn't support time in WHERE clause"}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show tag values cardinality with key and where matches the regular expression`,
command: `SHOW TAG VALUES CARDINALITY WITH KEY = host WHERE region =~ /ca.*/`,
exp: `{"results":[{"statement_id":0,"series":[{"name":"disk","columns":["count"],"values":[[1]]},{"name":"gpu","columns":["count"],"values":[[1]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show tag values cardinality with key and where does not match the regular expression`,
command: `SHOW TAG VALUES CARDINALITY WITH KEY = region WHERE host !~ /server0[12]/`,
exp: `{"results":[{"statement_id":0,"series":[{"name":"disk","columns":["count"],"values":[[1]]},{"name":"gpu","columns":["count"],"values":[[1]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show tag values cardinality with key and where partially matches the regular expression`,
command: `SHOW TAG VALUES CARDINALITY WITH KEY = host WHERE region =~ /us/`,
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[2]]},{"name":"gpu","columns":["count"],"values":[[1]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show tag values cardinality with key and where partially does not match the regular expression`,
command: `SHOW TAG VALUES CARDINALITY WITH KEY = host WHERE region !~ /us/`,
exp: `{"results":[{"statement_id":0,"series":[{"name":"disk","columns":["count"],"values":[[1]]},{"name":"gpu","columns":["count"],"values":[[1]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show tag values cardinality with key in and where does not match the regular expression`,
command: `SHOW TAG VALUES CARDINALITY FROM cpu WITH KEY IN (host, region) WHERE region = 'uswest'`,
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[2]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show tag values cardinality with key regex and where does not match the regular expression`,
command: `SHOW TAG VALUES CARDINALITY FROM cpu WITH KEY =~ /(host|region)/ WHERE region = 'uswest'`,
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[2]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show tag values cardinality with key and measurement matches regular expression`,
command: `SHOW TAG VALUES CARDINALITY FROM /[cg]pu/ WITH KEY = host`,
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[2]]},{"name":"gpu","columns":["count"],"values":[[2]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show tag values exact cardinality with key and where matches the regular expression`,
command: `SHOW TAG VALUES EXACT CARDINALITY WITH KEY = host WHERE region =~ /ca.*/`,
@ -7887,6 +8170,24 @@ func TestServer_Query_ShowFieldKeyCardinality(t *testing.T) {
}
test.addQueries([]*Query{
&Query{
name: `show field key cardinality`,
command: `SHOW FIELD KEY CARDINALITY`,
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[3]]},{"name":"disk","columns":["count"],"values":[[2]]},{"name":"gpu","columns":["count"],"values":[[4]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show field key cardinality from measurement`,
command: `SHOW FIELD KEY CARDINALITY FROM cpu`,
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[3]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show field key cardinality measurement with regex`,
command: `SHOW FIELD KEY CARDINALITY FROM /[cg]pu/`,
exp: `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["count"],"values":[[3]]},{"name":"gpu","columns":["count"],"values":[[4]]}]}]}`,
params: url.Values{"db": []string{"db0"}},
},
&Query{
name: `show field key exact cardinality`,
command: `SHOW FIELD KEY EXACT CARDINALITY`,