diff --git a/cmd/influxd/run/server_test.go b/cmd/influxd/run/server_test.go index 0f3bf7c6dd..d4d62d1f10 100644 --- a/cmd/influxd/run/server_test.go +++ b/cmd/influxd/run/server_test.go @@ -2322,6 +2322,65 @@ func TestServer_Query_Wildcards(t *testing.T) { } } +func TestServer_Query_WildcardExpansion(t *testing.T) { + t.Parallel() + s := OpenServer(NewConfig(), "") + defer s.Close() + + if err := s.CreateDatabaseAndRetentionPolicy("db0", newRetentionPolicyInfo("rp0", 1, 0)); err != nil { + t.Fatal(err) + } + if err := s.MetaStore.SetDefaultRetentionPolicy("db0", "rp0"); err != nil { + t.Fatal(err) + } + + writes := []string{ + fmt.Sprintf(`wildcard,region=us-east,host=A value=10,cpu=80 %d`, mustParseTime(time.RFC3339Nano, "2000-01-01T00:00:00Z").UnixNano()), + fmt.Sprintf(`wildcard,region=us-east,host=B value=20,cpu=90 %d`, mustParseTime(time.RFC3339Nano, "2000-01-01T00:00:10Z").UnixNano()), + fmt.Sprintf(`wildcard,region=us-west,host=B value=30,cpu=70 %d`, mustParseTime(time.RFC3339Nano, "2000-01-01T00:00:20Z").UnixNano()), + fmt.Sprintf(`wildcard,region=us-east,host=A value=40,cpu=60 %d`, mustParseTime(time.RFC3339Nano, "2000-01-01T00:00:30Z").UnixNano()), + + fmt.Sprintf(`dupnames,region=us-east,day=1 value=10,day=3i %d`, mustParseTime(time.RFC3339Nano, "2000-01-01T00:00:00Z").UnixNano()), + fmt.Sprintf(`dupnames,region=us-east,day=2 value=20,day=2i %d`, mustParseTime(time.RFC3339Nano, "2000-01-01T00:00:10Z").UnixNano()), + fmt.Sprintf(`dupnames,region=us-west,day=3 value=30,day=1i %d`, mustParseTime(time.RFC3339Nano, "2000-01-01T00:00:20Z").UnixNano()), + } + + test := NewTest("db0", "rp0") + test.write = strings.Join(writes, "\n") + + test.addQueries([]*Query{ + &Query{ + name: "wildcard", + params: url.Values{"db": []string{"db0"}}, + command: `SELECT * FROM wildcard`, + exp: `{"results":[{"series":[{"name":"wildcard","columns":["time","cpu","host","region","value"],"values":[["2000-01-01T00:00:00Z",80,"A","us-east",10],["2000-01-01T00:00:10Z",90,"B","us-east",20],["2000-01-01T00:00:20Z",70,"B","us-west",30]]}]}]}`, + }, + //&Query{ + //name: "duplicate tag and field name", + //params: url.Values{"db": []string{"db0"}}, + //command: `SELECT * FROM dupnames`, + //exp: `{}`, + //}, + }...) + + for i, query := range test.queries { + if i == 0 { + if err := test.init(s); err != nil { + t.Fatalf("test init failed: %s", err) + } + } + if query.skip { + t.Logf("SKIP:: %s", query.name) + continue + } + if err := query.Execute(s); err != nil { + t.Error(query.Error(err)) + } else if !query.success() { + t.Error(query.failureMessage()) + } + } +} + func TestServer_Query_AcrossShardsAndFields(t *testing.T) { t.Parallel() s := OpenServer(NewConfig(), "") diff --git a/influxql/ast.go b/influxql/ast.go index e782795481..5fce655f61 100644 --- a/influxql/ast.go +++ b/influxql/ast.go @@ -953,6 +953,31 @@ func (s *SelectStatement) HasWildcard() bool { return false } +// HasFieldWildcard returns whether or not the select statement has at least 1 wildcard in the fields +func (s *SelectStatement) HasFieldWildcard() bool { + for _, f := range s.Fields { + _, ok := f.Expr.(*Wildcard) + if ok { + return true + } + } + + return false +} + +// HasDimensionWildcard returns whether or not the select statement has +// at least 1 wildcard in the dimensions aka `GROUP BY` +func (s *SelectStatement) HasDimensionWildcard() bool { + for _, d := range s.Dimensions { + _, ok := d.Expr.(*Wildcard) + if ok { + return true + } + } + + return false +} + // hasTimeDimensions returns whether or not the select statement has at least 1 // where condition with time as the condition func (s *SelectStatement) hasTimeDimensions(node Node) bool { @@ -1344,6 +1369,17 @@ func (s *SelectStatement) NamesInSelect() []string { return a } +// NamesInDimension returns the field and tag names (idents) in the group by +func (s *SelectStatement) NamesInDimension() []string { + var a []string + + for _, d := range s.Dimensions { + a = append(a, walkNames(d.Expr)...) + } + + return a +} + // walkNames will walk the Expr and return the database fields func walkNames(exp Expr) []string { switch expr := exp.(type) { diff --git a/tsdb/executor.go b/tsdb/executor.go index bc1b84a9f7..00fced4f92 100644 --- a/tsdb/executor.go +++ b/tsdb/executor.go @@ -188,6 +188,7 @@ func (e *Executor) executeRaw(out chan *influxql.Row) { for { if m.bufferedChunk == nil { m.bufferedChunk, err = m.NextChunk() + //spew.Dump(m.bufferedChunk) if err != nil { out <- &influxql.Row{Err: err} return diff --git a/tsdb/mapper.go b/tsdb/mapper.go index b2cbb432dd..1d973f762e 100644 --- a/tsdb/mapper.go +++ b/tsdb/mapper.go @@ -443,6 +443,8 @@ func (lm *LocalMapper) rewriteSelectStatement(stmt *influxql.SelectStatement) (* // expandWildcards returns a new SelectStatement with wildcards in the fields // and/or GROUP BY expanded with actual field names. +// Tags and Measurements are included in the select if a `SELECT *` is issued +// There is no longer an explicit `GROUP BY *` when tags are used in the `SELECT` clause func (lm *LocalMapper) expandWildcards(stmt *influxql.SelectStatement) (*influxql.SelectStatement, error) { // If there are no wildcards in the statement, return it as-is. if !stmt.HasWildcard() { @@ -471,13 +473,27 @@ func (lm *LocalMapper) expandWildcards(stmt *influxql.SelectStatement) (*influxq fieldSet[name] = struct{}{} fields = append(fields, &influxql.Field{Expr: &influxql.VarRef{Val: name}}) } - // Get the dimensions for this measurement. - for _, t := range mm.TagKeys() { - if _, ok := dimensionSet[t]; ok { - continue + + // Add tags to fields if a wildcard was provided. + if stmt.HasFieldWildcard() { + for _, t := range mm.TagKeys() { + if _, ok := fieldSet[t]; ok { + continue + } + fieldSet[t] = struct{}{} + fields = append(fields, &influxql.Field{Expr: &influxql.VarRef{Val: t}}) + } + } + + // Get the dimensions for this measurement. + if stmt.HasDimensionWildcard() { + for _, t := range mm.TagKeys() { + if _, ok := dimensionSet[t]; ok { + continue + } + dimensionSet[t] = struct{}{} + dimensions = append(dimensions, &influxql.Dimension{Expr: &influxql.VarRef{Val: t}}) } - dimensionSet[t] = struct{}{} - dimensions = append(dimensions, &influxql.Dimension{Expr: &influxql.VarRef{Val: t}}) } } } @@ -789,9 +805,15 @@ func createTagSetsAndFields(m *Measurement, stmt *influxql.SelectStatement) (*ta } if m.HasTagKey(n) { sts.add(n) + } + } + + for _, n := range stmt.NamesInDimension() { + if m.HasTagKey(n) { tagKeys = append(tagKeys, n) } } + for _, n := range stmt.NamesInWhere() { if n == "time" { continue