Ensure column names get implicitly renamed with conflicts
parent
8fc6a0f648
commit
a8d637b03c
|
@ -1296,12 +1296,12 @@ func TestServer_Query_Alias(t *testing.T) {
|
|||
&Query{
|
||||
name: "double aggregate sum - SELECT sum(value), sum(steps) FROM db0.rp0.cpu",
|
||||
command: `SELECT sum(value), sum(steps) FROM db0.rp0.cpu`,
|
||||
exp: `{"results":[{"series":[{"name":"cpu","columns":["time","sum","sum"],"values":[["1970-01-01T00:00:00Z",3,7]]}]}]}`,
|
||||
exp: `{"results":[{"series":[{"name":"cpu","columns":["time","sum","sum_1"],"values":[["1970-01-01T00:00:00Z",3,7]]}]}]}`,
|
||||
},
|
||||
&Query{
|
||||
name: "double aggregate sum reverse order - SELECT sum(steps), sum(value) FROM db0.rp0.cpu",
|
||||
command: `SELECT sum(steps), sum(value) FROM db0.rp0.cpu`,
|
||||
exp: `{"results":[{"series":[{"name":"cpu","columns":["time","sum","sum"],"values":[["1970-01-01T00:00:00Z",7,3]]}]}]}`,
|
||||
exp: `{"results":[{"series":[{"name":"cpu","columns":["time","sum","sum_1"],"values":[["1970-01-01T00:00:00Z",7,3]]}]}]}`,
|
||||
},
|
||||
&Query{
|
||||
name: "double aggregate sum with alias - SELECT sum(value) as sumv, sum(steps) as sums FROM db0.rp0.cpu",
|
||||
|
@ -3805,13 +3805,13 @@ func TestServer_Query_Wildcards(t *testing.T) {
|
|||
name: "wildcard and field in select",
|
||||
params: url.Values{"db": []string{"db0"}},
|
||||
command: `SELECT value, * FROM wildcard`,
|
||||
exp: `{"results":[{"series":[{"name":"wildcard","columns":["time","value","region","value","valx"],"values":[["2000-01-01T00:00:00Z",10,"us-east",10,null],["2000-01-01T00:00:10Z",null,"us-east",null,20],["2000-01-01T00:00:20Z",30,"us-east",30,40]]}]}]}`,
|
||||
exp: `{"results":[{"series":[{"name":"wildcard","columns":["time","value","region","value_1","valx"],"values":[["2000-01-01T00:00:00Z",10,"us-east",10,null],["2000-01-01T00:00:10Z",null,"us-east",null,20],["2000-01-01T00:00:20Z",30,"us-east",30,40]]}]}]}`,
|
||||
},
|
||||
&Query{
|
||||
name: "field and wildcard in select",
|
||||
params: url.Values{"db": []string{"db0"}},
|
||||
command: `SELECT value, * FROM wildcard`,
|
||||
exp: `{"results":[{"series":[{"name":"wildcard","columns":["time","value","region","value","valx"],"values":[["2000-01-01T00:00:00Z",10,"us-east",10,null],["2000-01-01T00:00:10Z",null,"us-east",null,20],["2000-01-01T00:00:20Z",30,"us-east",30,40]]}]}]}`,
|
||||
exp: `{"results":[{"series":[{"name":"wildcard","columns":["time","value","region","value_1","valx"],"values":[["2000-01-01T00:00:00Z",10,"us-east",10,null],["2000-01-01T00:00:10Z",null,"us-east",null,20],["2000-01-01T00:00:20Z",30,"us-east",30,40]]}]}]}`,
|
||||
},
|
||||
&Query{
|
||||
name: "field and wildcard in group by",
|
||||
|
|
|
@ -9,8 +9,6 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/influxdb/pkg/slices"
|
||||
)
|
||||
|
||||
// DataType represents the primitive data types available in InfluxQL.
|
||||
|
@ -1036,34 +1034,70 @@ func (s *SelectStatement) RewriteTimeFields() {
|
|||
// ColumnNames will walk all fields and functions and return the appropriate field names for the select statement
|
||||
// while maintaining order of the field names
|
||||
func (s *SelectStatement) ColumnNames() []string {
|
||||
columnNames := []string{}
|
||||
|
||||
if !s.OmitTime {
|
||||
columnNames = append(columnNames, "time")
|
||||
}
|
||||
|
||||
// First walk each field
|
||||
// First walk each field to determine the number of columns.
|
||||
columnFields := Fields{}
|
||||
for _, field := range s.Fields {
|
||||
columnFields = append(columnFields, field)
|
||||
|
||||
switch f := field.Expr.(type) {
|
||||
case *Call:
|
||||
if f.Name == "top" || f.Name == "bottom" {
|
||||
if len(f.Args) == 2 {
|
||||
columnNames = append(columnNames, f.Name)
|
||||
continue
|
||||
for _, arg := range f.Args[1:] {
|
||||
ref, ok := arg.(*VarRef)
|
||||
if ok {
|
||||
columnFields = append(columnFields, &Field{Expr: ref})
|
||||
}
|
||||
}
|
||||
// We have a special case now where we have to add the column names for the fields TOP or BOTTOM asked for as well
|
||||
columnNames = slices.Union(columnNames, f.Fields(), true)
|
||||
continue
|
||||
}
|
||||
columnNames = append(columnNames, field.Name())
|
||||
default:
|
||||
// time is always first, and we already added it, so ignore it if they asked for it anywhere else.
|
||||
if field.Name() != "time" {
|
||||
columnNames = append(columnNames, field.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if we should add an extra column for an implicit time.
|
||||
offset := 0
|
||||
if !s.OmitTime {
|
||||
offset++
|
||||
}
|
||||
|
||||
columnNames := make([]string, len(columnFields)+offset)
|
||||
if !s.OmitTime {
|
||||
// Add the implicit time if requested.
|
||||
columnNames[0] = "time"
|
||||
}
|
||||
|
||||
// Keep track of the encountered column names.
|
||||
names := make(map[string]int)
|
||||
|
||||
// Resolve aliases first.
|
||||
for i, col := range columnFields {
|
||||
if col.Alias != "" {
|
||||
columnNames[i+offset] = col.Alias
|
||||
names[col.Alias] = 1
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve any generated names and resolve conflicts.
|
||||
for i, col := range columnFields {
|
||||
if columnNames[i+offset] != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
name := col.Name()
|
||||
count, conflict := names[name]
|
||||
if conflict {
|
||||
for {
|
||||
resolvedName := fmt.Sprintf("%s_%d", name, count)
|
||||
_, conflict = names[resolvedName]
|
||||
if !conflict {
|
||||
names[name] = count + 1
|
||||
name = resolvedName
|
||||
break
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
names[name] += 1
|
||||
columnNames[i+offset] = name
|
||||
}
|
||||
return columnNames
|
||||
}
|
||||
|
||||
|
|
|
@ -1158,6 +1158,57 @@ func Test_fieldsNames(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestSelect_ColumnNames(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
stmt *influxql.SelectStatement
|
||||
columns []string
|
||||
}{
|
||||
{
|
||||
stmt: &influxql.SelectStatement{
|
||||
Fields: influxql.Fields([]*influxql.Field{
|
||||
{Expr: &influxql.VarRef{Val: "value"}},
|
||||
}),
|
||||
},
|
||||
columns: []string{"time", "value"},
|
||||
},
|
||||
{
|
||||
stmt: &influxql.SelectStatement{
|
||||
Fields: influxql.Fields([]*influxql.Field{
|
||||
{Expr: &influxql.VarRef{Val: "value"}},
|
||||
{Expr: &influxql.VarRef{Val: "value"}},
|
||||
{Expr: &influxql.VarRef{Val: "value_1"}},
|
||||
}),
|
||||
},
|
||||
columns: []string{"time", "value", "value_1", "value_1_1"},
|
||||
},
|
||||
{
|
||||
stmt: &influxql.SelectStatement{
|
||||
Fields: influxql.Fields([]*influxql.Field{
|
||||
{Expr: &influxql.VarRef{Val: "value"}},
|
||||
{Expr: &influxql.VarRef{Val: "value_1"}},
|
||||
{Expr: &influxql.VarRef{Val: "value"}},
|
||||
}),
|
||||
},
|
||||
columns: []string{"time", "value", "value_1", "value_2"},
|
||||
},
|
||||
{
|
||||
stmt: &influxql.SelectStatement{
|
||||
Fields: influxql.Fields([]*influxql.Field{
|
||||
{Expr: &influxql.VarRef{Val: "value"}},
|
||||
{Expr: &influxql.VarRef{Val: "total"}, Alias: "value"},
|
||||
{Expr: &influxql.VarRef{Val: "value"}},
|
||||
}),
|
||||
},
|
||||
columns: []string{"time", "value_1", "value", "value_2"},
|
||||
},
|
||||
} {
|
||||
columns := tt.stmt.ColumnNames()
|
||||
if !reflect.DeepEqual(columns, tt.columns) {
|
||||
t.Errorf("%d. expected %s, got %s", i, tt.columns, columns)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSources_Names(t *testing.T) {
|
||||
sources := influxql.Sources([]influxql.Source{
|
||||
&influxql.Measurement{
|
||||
|
|
Loading…
Reference in New Issue