Assign a name to columns with binary expressions in them

The name of the column will be every measurement located inside of the
math expression in the order they are encountered in within the
expression.

Also handle `*influxql.ParenExpr` in the function
`(*influxql.Field).Name()`

Fixes #5730.
pull/5732/head
Jonathan A. Sternberg 2016-02-17 19:24:13 -05:00
parent 7479584afd
commit 983f810539
3 changed files with 72 additions and 23 deletions

View File

@ -807,37 +807,37 @@ func TestServer_Query_Math(t *testing.T) {
&Query{
name: "SELECT multiple of float value",
command: `SELECT value * 2 from db.rp.float`,
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"float","columns":["time",""],"values":[["%s",84]]}]}]}`, now.Format(time.RFC3339Nano)),
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"float","columns":["time","value"],"values":[["%s",84]]}]}]}`, now.Format(time.RFC3339Nano)),
},
&Query{
name: "SELECT multiple of float value",
command: `SELECT 2 * value from db.rp.float`,
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"float","columns":["time",""],"values":[["%s",84]]}]}]}`, now.Format(time.RFC3339Nano)),
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"float","columns":["time","value"],"values":[["%s",84]]}]}]}`, now.Format(time.RFC3339Nano)),
},
&Query{
name: "SELECT multiple of integer value",
command: `SELECT value * 2 from db.rp.integer`,
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time",""],"values":[["%s",84]]}]}]}`, now.Format(time.RFC3339Nano)),
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time","value"],"values":[["%s",84]]}]}]}`, now.Format(time.RFC3339Nano)),
},
&Query{
name: "SELECT float multiple of integer value",
command: `SELECT value * 2.0 from db.rp.integer`,
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time",""],"values":[["%s",84]]}]}]}`, now.Format(time.RFC3339Nano)),
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time","value"],"values":[["%s",84]]}]}]}`, now.Format(time.RFC3339Nano)),
},
&Query{
name: "SELECT square of float value",
command: `SELECT value * value from db.rp.float`,
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"float","columns":["time",""],"values":[["%s",1764]]}]}]}`, now.Format(time.RFC3339Nano)),
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"float","columns":["time","value_value"],"values":[["%s",1764]]}]}]}`, now.Format(time.RFC3339Nano)),
},
&Query{
name: "SELECT square of integer value",
command: `SELECT value * value from db.rp.integer`,
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time",""],"values":[["%s",1764]]}]}]}`, now.Format(time.RFC3339Nano)),
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time","value_value"],"values":[["%s",1764]]}]}]}`, now.Format(time.RFC3339Nano)),
},
&Query{
name: "SELECT square of integer, float value",
command: `SELECT value * value,float from db.rp.integer`,
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time","","float"],"values":[["%s",1764,null]]}]}]}`, now.Format(time.RFC3339Nano)),
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time","value_value","float"],"values":[["%s",1764,null]]}]}]}`, now.Format(time.RFC3339Nano)),
},
&Query{
name: "SELECT square of integer value with alias",
@ -847,17 +847,17 @@ func TestServer_Query_Math(t *testing.T) {
&Query{
name: "SELECT sum of aggregates",
command: `SELECT max(value) + min(value) from db.rp.integer`,
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time",""],"values":[["1970-01-01T00:00:00Z",84]]}]}]}`),
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time","max_min"],"values":[["1970-01-01T00:00:00Z",84]]}]}]}`),
},
&Query{
name: "SELECT square of enclosed integer value",
command: `SELECT ((value) * (value)) from db.rp.integer`,
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time",""],"values":[["%s",1764]]}]}]}`, now.Format(time.RFC3339Nano)),
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time","value_value"],"values":[["%s",1764]]}]}]}`, now.Format(time.RFC3339Nano)),
},
&Query{
name: "SELECT square of enclosed integer value",
command: `SELECT (value * value) from db.rp.integer`,
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time",""],"values":[["%s",1764]]}]}]}`, now.Format(time.RFC3339Nano)),
exp: fmt.Sprintf(`{"results":[{"series":[{"name":"integer","columns":["time","value_value"],"values":[["%s",1764]]}]}]}`, now.Format(time.RFC3339Nano)),
},
}...)
@ -2871,13 +2871,13 @@ func TestServer_Query_Aggregates_Load(t *testing.T) {
name: "group by multiple dimensions",
params: url.Values{"db": []string{"db0"}},
command: `SELECT sum(value)*2 FROM load`,
exp: `{"results":[{"series":[{"name":"load","columns":["time",""],"values":[["1970-01-01T00:00:00Z",300]]}]}]}`,
exp: `{"results":[{"series":[{"name":"load","columns":["time","sum"],"values":[["1970-01-01T00:00:00Z",300]]}]}]}`,
},
&Query{
name: "group by multiple dimensions",
params: url.Values{"db": []string{"db0"}},
command: `SELECT sum(value)/2 FROM load`,
exp: `{"results":[{"series":[{"name":"load","columns":["time",""],"values":[["1970-01-01T00:00:00Z",75]]}]}]}`,
exp: `{"results":[{"series":[{"name":"load","columns":["time","sum"],"values":[["1970-01-01T00:00:00Z",75]]}]}]}`,
},
}...)

View File

@ -2639,6 +2639,11 @@ func (f *Field) Name() string {
switch expr := f.Expr.(type) {
case *Call:
return expr.Name
case *BinaryExpr:
return BinaryExprName(expr)
case *ParenExpr:
f := Field{Expr: expr.Expr}
return f.Name()
case *VarRef:
return expr.Val
}
@ -2919,6 +2924,27 @@ func (e *BinaryExpr) String() string {
return fmt.Sprintf("%s %s %s", e.LHS.String(), e.Op.String(), e.RHS.String())
}
func BinaryExprName(expr *BinaryExpr) string {
v := binaryExprNameVisitor{}
Walk(&v, expr)
return strings.Join(v.names, "_")
}
type binaryExprNameVisitor struct {
names []string
}
func (v *binaryExprNameVisitor) Visit(n Node) Visitor {
switch n := n.(type) {
case *VarRef:
v.names = append(v.names, n.Val)
case *Call:
v.names = append(v.names, n.Name)
return nil
}
return v
}
// ParenExpr represents a parenthesized expression.
type ParenExpr struct {
Expr Expr

View File

@ -717,6 +717,29 @@ func TestSelectStatement_HasCountDistinct(t *testing.T) {
}
}
// Ensure binary expression names can be evaluated.
func TestBinaryExprName(t *testing.T) {
for i, tt := range []struct {
expr string
name string
}{
{expr: `value + 1`, name: `value`},
{expr: `"user" / total`, name: `user_total`},
{expr: `("user" + total) / total`, name: `user_total_total`},
} {
expr := influxql.MustParseExpr(tt.expr)
switch expr := expr.(type) {
case *influxql.BinaryExpr:
name := influxql.BinaryExprName(expr)
if name != tt.name {
t.Errorf("%d. unexpected name %s, got %s", i, name, tt.name)
}
default:
t.Errorf("%d. unexpected expr type: %T", i, expr)
}
}
}
// Ensure the time range of an expression can be extracted.
func TestTimeRange(t *testing.T) {
for i, tt := range []struct {
@ -1070,12 +1093,12 @@ func Test_fieldsNames(t *testing.T) {
{ //case: binary expr(valRef)
in: []string{"value+value"},
out: []string{"value", "value"},
alias: []string{""},
alias: []string{"value_value"},
},
{ //case: binary expr + valRef
in: []string{"value+value", "temperature"},
out: []string{"value", "value", "temperature"},
alias: []string{"", "temperature"},
alias: []string{"value_value", "temperature"},
},
{ //case: aggregate expr
in: []string{"mean(value)"},
@ -1085,37 +1108,37 @@ func Test_fieldsNames(t *testing.T) {
{ //case: binary expr(aggregate expr)
in: []string{"mean(value) + max(value)"},
out: []string{"value", "value"},
alias: []string{""},
alias: []string{"mean_max"},
},
{ //case: binary expr(aggregate expr) + valRef
in: []string{"mean(value) + max(value)", "temperature"},
out: []string{"value", "value", "temperature"},
alias: []string{"", "temperature"},
alias: []string{"mean_max", "temperature"},
},
{ //case: mixed aggregate and varRef
in: []string{"mean(value) + temperature"},
out: []string{"value", "temperature"},
alias: []string{""},
alias: []string{"mean_temperature"},
},
{ //case: ParenExpr(varRef)
in: []string{"(value)"},
out: []string{"value"},
alias: []string{""},
alias: []string{"value"},
},
{ //case: ParenExpr(varRef + varRef)
in: []string{"(value + value)"},
out: []string{"value", "value"},
alias: []string{""},
alias: []string{"value_value"},
},
{ //case: ParenExpr(aggregate)
in: []string{"(mean(value))"},
out: []string{"value"},
alias: []string{""},
alias: []string{"mean"},
},
{ //case: ParenExpr(aggregate + aggregate)
in: []string{"(mean(value) + max(value))"},
out: []string{"value", "value"},
alias: []string{""},
alias: []string{"mean_max"},
},
} {
fields := influxql.Fields{}
@ -1125,11 +1148,11 @@ func Test_fieldsNames(t *testing.T) {
}
got := fields.Names()
if !reflect.DeepEqual(got, test.out) {
t.Errorf("get fileds name:\nexp=%v\ngot=%v\n", test.out, got)
t.Errorf("get fields name:\nexp=%v\ngot=%v\n", test.out, got)
}
alias := fields.AliasNames()
if !reflect.DeepEqual(alias, test.alias) {
t.Errorf("get fileds alias name:\nexp=%v\ngot=%v\n", test.alias, alias)
t.Errorf("get fields alias name:\nexp=%v\ngot=%v\n", test.alias, alias)
}
}