Browse Source

add __timeGroup macro for mysql (#9596)

* add __timeGroup macro for mysql

* put example __timeGroup query in frontend help

* do __timeGroup interval parsing in go similar to mysql

* ignore whitespace around interval
Sven Klemm 8 years ago
parent
commit
34da0711ab

+ 12 - 1
pkg/tsdb/mysql/macros.go

@@ -3,6 +3,8 @@ package mysql
 import (
 import (
 	"fmt"
 	"fmt"
 	"regexp"
 	"regexp"
+	"strings"
+	"time"
 
 
 	"github.com/grafana/grafana/pkg/tsdb"
 	"github.com/grafana/grafana/pkg/tsdb"
 )
 )
@@ -25,7 +27,7 @@ func (m *MySqlMacroEngine) Interpolate(timeRange *tsdb.TimeRange, sql string) (s
 	var macroError error
 	var macroError error
 
 
 	sql = replaceAllStringSubmatchFunc(rExp, sql, func(groups []string) string {
 	sql = replaceAllStringSubmatchFunc(rExp, sql, func(groups []string) string {
-		res, err := m.evaluateMacro(groups[1], groups[2:])
+		res, err := m.evaluateMacro(groups[1], strings.Split(groups[2], ","))
 		if err != nil && macroError == nil {
 		if err != nil && macroError == nil {
 			macroError = err
 			macroError = err
 			return "macro_error()"
 			return "macro_error()"
@@ -73,6 +75,15 @@ func (m *MySqlMacroEngine) evaluateMacro(name string, args []string) (string, er
 		return fmt.Sprintf("FROM_UNIXTIME(%d)", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil
 		return fmt.Sprintf("FROM_UNIXTIME(%d)", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil
 	case "__timeTo":
 	case "__timeTo":
 		return fmt.Sprintf("FROM_UNIXTIME(%d)", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil
 		return fmt.Sprintf("FROM_UNIXTIME(%d)", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil
+	case "__timeGroup":
+		if len(args) != 2 {
+			return "", fmt.Errorf("macro %v needs time column and interval", name)
+		}
+		interval, err := time.ParseDuration(strings.Trim(args[1], `'" `))
+		if err != nil {
+			return "", fmt.Errorf("error parsing interval %v", args[1])
+		}
+		return fmt.Sprintf("cast(cast(UNIX_TIMESTAMP(%s)/(%.0f) as signed)*%.0f as signed)", args[0], interval.Seconds(), interval.Seconds()), nil
 	case "__unixEpochFilter":
 	case "__unixEpochFilter":
 		if len(args) == 0 {
 		if len(args) == 0 {
 			return "", fmt.Errorf("missing time column argument for macro %v", name)
 			return "", fmt.Errorf("missing time column argument for macro %v", name)

+ 8 - 0
pkg/tsdb/mysql/macros_test.go

@@ -40,6 +40,14 @@ func TestMacroEngine(t *testing.T) {
 			So(sql, ShouldEqual, "select FROM_UNIXTIME(18446744066914186738)")
 			So(sql, ShouldEqual, "select FROM_UNIXTIME(18446744066914186738)")
 		})
 		})
 
 
+		Convey("interpolate __timeGroup function", func() {
+
+			sql, err := engine.Interpolate(timeRange, "GROUP BY $__timeGroup(time_column,'5m')")
+			So(err, ShouldBeNil)
+
+			So(sql, ShouldEqual, "GROUP BY cast(cast(UNIX_TIMESTAMP(time_column)/(300) as signed)*300 as signed)")
+		})
+
 		Convey("interpolate __timeTo function", func() {
 		Convey("interpolate __timeTo function", func() {
 			sql, err := engine.Interpolate(timeRange, "select $__timeTo(time_column)")
 			sql, err := engine.Interpolate(timeRange, "select $__timeTo(time_column)")
 			So(err, ShouldBeNil)
 			So(err, ShouldBeNil)

+ 7 - 2
pkg/tsdb/postgres/macros.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"fmt"
 	"regexp"
 	"regexp"
 	"strings"
 	"strings"
+	"time"
 
 
 	"github.com/grafana/grafana/pkg/tsdb"
 	"github.com/grafana/grafana/pkg/tsdb"
 )
 )
@@ -80,10 +81,14 @@ func (m *PostgresMacroEngine) evaluateMacro(name string, args []string) (string,
 	case "__timeTo":
 	case "__timeTo":
 		return fmt.Sprintf("to_timestamp(%d)", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil
 		return fmt.Sprintf("to_timestamp(%d)", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil
 	case "__timeGroup":
 	case "__timeGroup":
-		if len(args) < 2 {
+		if len(args) != 2 {
 			return "", fmt.Errorf("macro %v needs time column and interval", name)
 			return "", fmt.Errorf("macro %v needs time column and interval", name)
 		}
 		}
-		return fmt.Sprintf("(extract(epoch from \"%s\")/extract(epoch from %s::interval))::int*extract(epoch from %s::interval)", args[0], args[1], args[1]), nil
+		interval, err := time.ParseDuration(strings.Trim(args[1], `' `))
+		if err != nil {
+			return "", fmt.Errorf("error parsing interval %v", args[1])
+		}
+		return fmt.Sprintf("(extract(epoch from \"%s\")/%v)::bigint*%v", args[0], interval.Seconds(), interval.Seconds()), nil
 	case "__unixEpochFilter":
 	case "__unixEpochFilter":
 		if len(args) == 0 {
 		if len(args) == 0 {
 			return "", fmt.Errorf("missing time column argument for macro %v", name)
 			return "", fmt.Errorf("missing time column argument for macro %v", name)

+ 1 - 1
pkg/tsdb/postgres/macros_test.go

@@ -45,7 +45,7 @@ func TestMacroEngine(t *testing.T) {
 			sql, err := engine.Interpolate(timeRange, "GROUP BY $__timeGroup(time_column,'5m')")
 			sql, err := engine.Interpolate(timeRange, "GROUP BY $__timeGroup(time_column,'5m')")
 			So(err, ShouldBeNil)
 			So(err, ShouldBeNil)
 
 
-			So(sql, ShouldEqual, "GROUP BY (extract(epoch from \"time_column\")/extract(epoch from '5m'::interval))::int*extract(epoch from '5m'::interval)")
+			So(sql, ShouldEqual, "GROUP BY (extract(epoch from \"time_column\")/300)::bigint*300")
 		})
 		})
 
 
 		Convey("interpolate __timeTo function", func() {
 		Convey("interpolate __timeTo function", func() {

+ 9 - 1
public/app/plugins/datasource/mysql/partials/query.editor.html

@@ -49,7 +49,15 @@ Macros:
 - $__time(column) -&gt; UNIX_TIMESTAMP(column) as time_sec
 - $__time(column) -&gt; UNIX_TIMESTAMP(column) as time_sec
 - $__timeFilter(column) -&gt;  UNIX_TIMESTAMP(time_date_time) &ge; 1492750877 AND UNIX_TIMESTAMP(time_date_time) &le; 1492750877
 - $__timeFilter(column) -&gt;  UNIX_TIMESTAMP(time_date_time) &ge; 1492750877 AND UNIX_TIMESTAMP(time_date_time) &le; 1492750877
 - $__unixEpochFilter(column) -&gt;  time_unix_epoch &gt; 1492750877 AND time_unix_epoch &lt; 1492750877
 - $__unixEpochFilter(column) -&gt;  time_unix_epoch &gt; 1492750877 AND time_unix_epoch &lt; 1492750877
-- $__timeGroup(column,'5m') -&gt; (extract(epoch from "dateColumn")/extract(epoch from '5m'::interval))::int
+- $__timeGroup(column,'5m') -&gt; cast(cast(UNIX_TIMESTAMP(column)/(300) as signed)*300 as signed)
+
+Example of group by and order by with $__timeGroup:
+SELECT
+  $__timeGroup(timestamp_col, '1h') AS time,
+  sum(value_double) as value
+FROM yourtable
+GROUP BY 1
+ORDER BY 1
 
 
 Or build your own conditionals using these macros which just return the values:
 Or build your own conditionals using these macros which just return the values:
 - $__timeFrom() -&gt;  FROM_UNIXTIME(1492750877)
 - $__timeFrom() -&gt;  FROM_UNIXTIME(1492750877)

+ 5 - 7
public/app/plugins/datasource/postgres/partials/query.editor.html

@@ -50,17 +50,15 @@ Macros:
 - $__timeEpoch -&gt; extract(epoch from column) as "time"
 - $__timeEpoch -&gt; extract(epoch from column) as "time"
 - $__timeFilter(column) -&gt;  column &ge; to_timestamp(1492750877) AND column &le; to_timestamp(1492750877)
 - $__timeFilter(column) -&gt;  column &ge; to_timestamp(1492750877) AND column &le; to_timestamp(1492750877)
 - $__unixEpochFilter(column) -&gt;  column &gt; 1492750877 AND column &lt; 1492750877
 - $__unixEpochFilter(column) -&gt;  column &gt; 1492750877 AND column &lt; 1492750877
-
-To group by time use $__timeGroup:
--&gt; (extract(epoch from column)/extract(epoch from column::interval))::int
+- $__timeGroup(column,'5m') -&gt; (extract(epoch from "dateColumn")/extract(epoch from '5m'::interval))::int
 
 
 Example of group by and order by with $__timeGroup:
 Example of group by and order by with $__timeGroup:
 SELECT
 SELECT
-  min(date_time_col) AS time_sec,
-  sum(value_double) as value
+  $__timeGroup(date_time_col, '1h') AS time,
+  sum(value) as value
 FROM yourtable
 FROM yourtable
-group by $__timeGroup(date_time_col, '1h')
-order by $__timeGroup(date_time_col, '1h') ASC
+GROUP BY time
+ORDER BY time
 
 
 Or build your own conditionals using these macros which just return the values:
 Or build your own conditionals using these macros which just return the values:
 - $__timeFrom() -&gt;  to_timestamp(1492750877)
 - $__timeFrom() -&gt;  to_timestamp(1492750877)