Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
d6d2080f
Commit
d6d2080f
authored
Mar 31, 2017
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
mysql: minor progress on response processing
parent
bd4f0734
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
196 additions
and
120 deletions
+196
-120
pkg/api/metrics.go
+6
-0
pkg/models/test_data.go
+9
-10
pkg/services/sqlstore/migrations/stats_mig.go
+2
-2
pkg/services/sqlstore/sql_test_data.go
+43
-12
pkg/tsdb/models.go
+4
-3
pkg/tsdb/mysql/mysql.go
+90
-91
pkg/tsdb/request.go
+3
-1
public/app/plugins/datasource/mysql/datasource.ts
+5
-0
public/app/plugins/datasource/mysql/module.ts
+15
-0
public/app/plugins/datasource/mysql/partials/query.editor.html
+19
-1
No files found.
pkg/api/metrics.go
View file @
d6d2080f
...
@@ -50,6 +50,12 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
...
@@ -50,6 +50,12 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
return
ApiError
(
500
,
"Metric request error"
,
err
)
return
ApiError
(
500
,
"Metric request error"
,
err
)
}
}
for
_
,
res
:=
range
resp
.
Results
{
if
res
.
Error
!=
nil
{
res
.
ErrorString
=
res
.
Error
.
Error
()
}
}
return
Json
(
200
,
&
resp
)
return
Json
(
200
,
&
resp
)
}
}
...
...
pkg/models/test_data.go
View file @
d6d2080f
...
@@ -6,14 +6,13 @@ type InsertSqlTestDataCommand struct {
...
@@ -6,14 +6,13 @@ type InsertSqlTestDataCommand struct {
}
}
type
SqlTestData
struct
{
type
SqlTestData
struct
{
Id
int64
Id
int64
Metric1
string
Metric1
string
Metric2
string
Metric2
string
ValueBigInt
int64
ValueBigInt
int64
ValueDouble
float64
ValueDouble
float64
ValueFloat
float32
ValueFloat
float32
ValueInt
int
ValueInt
int
TimeEpoch
int64
TimeEpoch
int64
TimeDateTime
time
.
Time
TimeDateTime
time
.
Time
TimeTimeStamp
time
.
Time
}
}
pkg/services/sqlstore/migrations/stats_mig.go
View file @
d6d2080f
...
@@ -46,8 +46,8 @@ func addTestDataMigrations(mg *Migrator) {
...
@@ -46,8 +46,8 @@ func addTestDataMigrations(mg *Migrator) {
{
Name
:
"value_float"
,
Type
:
DB_Float
,
Nullable
:
true
},
{
Name
:
"value_float"
,
Type
:
DB_Float
,
Nullable
:
true
},
{
Name
:
"value_int"
,
Type
:
DB_Int
,
Nullable
:
true
},
{
Name
:
"value_int"
,
Type
:
DB_Int
,
Nullable
:
true
},
{
Name
:
"time_epoch"
,
Type
:
DB_BigInt
,
Nullable
:
false
},
{
Name
:
"time_epoch"
,
Type
:
DB_BigInt
,
Nullable
:
false
},
{
Name
:
"time_datetime"
,
Type
:
DB_DateTime
,
Nullable
:
false
},
{
Name
:
"time_date
_
time"
,
Type
:
DB_DateTime
,
Nullable
:
false
},
{
Name
:
"time_timestamp"
,
Type
:
DB_TimeStamp
,
Nullable
:
false
},
{
Name
:
"time_time
_
stamp"
,
Type
:
DB_TimeStamp
,
Nullable
:
false
},
},
},
}
}
...
...
pkg/services/sqlstore/sql_test_data.go
View file @
d6d2080f
package
sqlstore
package
sqlstore
import
(
import
(
"math/rand"
"time"
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/bus"
...
@@ -11,23 +12,53 @@ func init() {
...
@@ -11,23 +12,53 @@ func init() {
bus
.
AddHandler
(
"sql"
,
InsertSqlTestData
)
bus
.
AddHandler
(
"sql"
,
InsertSqlTestData
)
}
}
func
InsertSqlTestData
(
cmd
*
m
.
InsertSqlTestDataCommand
)
error
{
func
sqlRandomWalk
(
m1
string
,
m2
string
,
intWalker
int64
,
floatWalker
float64
,
sess
*
session
)
error
{
return
inTransaction2
(
func
(
sess
*
session
)
error
{
row
:=
&
m
.
SqlTestData
{
timeWalker
:=
time
.
Now
()
.
Add
(
time
.
Hour
*
-
1
)
Metric1
:
"server1"
,
now
:=
time
.
Now
()
Metric2
:
"frontend"
,
step
:=
time
.
Minute
ValueBigInt
:
123123
,
ValueDouble
:
3.14159265359
,
row
:=
&
m
.
SqlTestData
{
ValueFloat
:
3.14159265359
,
Metric1
:
m1
,
TimeEpoch
:
time
.
Now
()
.
Unix
(),
Metric2
:
m2
,
TimeDateTime
:
time
.
Now
(),
TimeEpoch
:
timeWalker
.
Unix
(),
}
TimeDateTime
:
timeWalker
,
}
for
timeWalker
.
Unix
()
<
now
.
Unix
()
{
timeWalker
=
timeWalker
.
Add
(
step
)
row
.
Id
=
0
row
.
ValueBigInt
+=
rand
.
Int63n
(
100
)
-
100
row
.
ValueDouble
+=
rand
.
Float64
()
-
0.5
row
.
ValueFloat
+=
rand
.
Float32
()
-
0.5
row
.
TimeEpoch
=
timeWalker
.
Unix
()
row
.
TimeDateTime
=
timeWalker
sqlog
.
Info
(
"Writing SQL test data row"
)
if
_
,
err
:=
sess
.
Table
(
"test_data"
)
.
Insert
(
row
);
err
!=
nil
{
if
_
,
err
:=
sess
.
Table
(
"test_data"
)
.
Insert
(
row
);
err
!=
nil
{
return
err
return
err
}
}
}
return
nil
}
func
InsertSqlTestData
(
cmd
*
m
.
InsertSqlTestDataCommand
)
error
{
return
inTransaction2
(
func
(
sess
*
session
)
error
{
var
err
error
sqlog
.
Info
(
"SQL TestData: Clearing previous test data"
)
res
,
err
:=
sess
.
Exec
(
"TRUNCATE test_data"
)
if
err
!=
nil
{
return
err
}
rows
,
_
:=
res
.
RowsAffected
()
sqlog
.
Info
(
"SQL TestData: Truncate done"
,
"rows"
,
rows
)
sqlRandomWalk
(
"server1"
,
"frontend"
,
100
,
1.123
,
sess
)
return
nil
return
err
})
})
}
}
pkg/tsdb/models.go
View file @
d6d2080f
...
@@ -45,9 +45,10 @@ func (br *BatchResult) WithError(err error) *BatchResult {
...
@@ -45,9 +45,10 @@ func (br *BatchResult) WithError(err error) *BatchResult {
}
}
type
QueryResult
struct
{
type
QueryResult
struct
{
Error
error
`json:"error"`
Error
error
`json:"-"`
RefId
string
`json:"refId"`
ErrorString
string
`json:"error"`
Series
TimeSeriesSlice
`json:"series"`
RefId
string
`json:"refId"`
Series
TimeSeriesSlice
`json:"series"`
}
}
type
TimeSeries
struct
{
type
TimeSeries
struct
{
...
...
pkg/tsdb/mysql/mysql.go
View file @
d6d2080f
...
@@ -4,9 +4,12 @@ import (
...
@@ -4,9 +4,12 @@ import (
"context"
"context"
"database/sql"
"database/sql"
"fmt"
"fmt"
"strconv"
"sync"
"sync"
"github.com/go-xorm/core"
"github.com/go-xorm/xorm"
"github.com/go-xorm/xorm"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
"github.com/grafana/grafana/pkg/tsdb"
...
@@ -74,19 +77,14 @@ func (e *MysqlExecutor) initEngine() error {
...
@@ -74,19 +77,14 @@ func (e *MysqlExecutor) initEngine() error {
}
}
func
(
e
*
MysqlExecutor
)
Execute
(
ctx
context
.
Context
,
queries
tsdb
.
QuerySlice
,
context
*
tsdb
.
QueryContext
)
*
tsdb
.
BatchResult
{
func
(
e
*
MysqlExecutor
)
Execute
(
ctx
context
.
Context
,
queries
tsdb
.
QuerySlice
,
context
*
tsdb
.
QueryContext
)
*
tsdb
.
BatchResult
{
result
:=
&
tsdb
.
BatchResult
{}
result
:=
&
tsdb
.
BatchResult
{
QueryResults
:
make
(
map
[
string
]
*
tsdb
.
QueryResult
),
}
session
:=
e
.
engine
.
NewSession
()
session
:=
e
.
engine
.
NewSession
()
defer
session
.
Close
()
defer
session
.
Close
()
db
:=
session
.
DB
()
db
:=
session
.
DB
()
// queries := strings.Split(req.Query, ";")
//
// data := dataStruct{}
// data.Results = make([]resultsStruct, 1)
// data.Results[0].Series = make([]seriesStruct, 0)
for
_
,
query
:=
range
queries
{
for
_
,
query
:=
range
queries
{
rawSql
:=
query
.
Model
.
Get
(
"rawSql"
)
.
MustString
()
rawSql
:=
query
.
Model
.
Get
(
"rawSql"
)
.
MustString
()
if
rawSql
==
""
{
if
rawSql
==
""
{
...
@@ -100,118 +98,119 @@ func (e *MysqlExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, co
...
@@ -100,118 +98,119 @@ func (e *MysqlExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, co
}
}
defer
rows
.
Close
()
defer
rows
.
Close
()
columnNames
,
err
:=
rows
.
Columns
()
result
.
QueryResults
[
query
.
RefId
]
=
e
.
TransformToTimeSeries
(
query
,
rows
)
}
for
_
,
value
:=
range
result
.
QueryResults
{
if
value
.
Error
!=
nil
{
e
.
log
.
Error
(
"error"
,
"error"
,
value
.
Error
)
}
}
return
result
}
func
(
e
MysqlExecutor
)
TransformToTimeSeries
(
query
*
tsdb
.
Query
,
rows
*
core
.
Rows
)
*
tsdb
.
QueryResult
{
result
:=
&
tsdb
.
QueryResult
{
RefId
:
query
.
RefId
}
pointsBySeries
:=
make
(
map
[
string
]
*
tsdb
.
TimeSeries
)
columnNames
,
err
:=
rows
.
Columns
()
if
err
!=
nil
{
result
.
Error
=
err
return
result
}
rowData
:=
NewStringStringScan
(
columnNames
)
for
rows
.
Next
()
{
err
:=
rowData
.
Update
(
rows
.
Rows
)
if
err
!=
nil
{
if
err
!=
nil
{
e
.
log
.
Error
(
"Mysql response parsing"
,
"error"
,
err
)
result
.
Error
=
err
result
.
Error
=
err
return
result
return
result
}
}
rc
:=
NewStringStringScan
(
columnNames
)
if
rowData
.
metric
==
""
{
for
rows
.
Next
()
{
rowData
.
metric
=
"Unknown"
err
:=
rc
.
Update
(
rows
.
Rows
)
}
if
err
!=
nil
{
e
.
log
.
Error
(
"Mysql response parsing"
,
"error"
,
err
)
e
.
log
.
Info
(
"Rows"
,
"metric"
,
rowData
.
metric
,
"time"
,
rowData
.
time
,
"value"
,
rowData
.
value
)
result
.
Error
=
err
return
result
if
!
rowData
.
time
.
Valid
{
}
result
.
Error
=
fmt
.
Errorf
(
"Found row with no time value"
)
return
result
}
rowValues
:=
rc
.
Get
()
if
series
,
exist
:=
pointsBySeries
[
rowData
.
metric
];
exist
{
e
.
log
.
Info
(
"Rows"
,
"row"
,
rowValues
)
series
.
Points
=
append
(
series
.
Points
,
tsdb
.
TimePoint
{
rowData
.
value
,
rowData
.
time
})
}
else
{
series
:=
&
tsdb
.
TimeSeries
{
Name
:
rowData
.
metric
}
series
.
Points
=
append
(
series
.
Points
,
tsdb
.
TimePoint
{
rowData
.
value
,
rowData
.
time
})
pointsBySeries
[
rowData
.
metric
]
=
series
}
}
}
// for rows.Next() {
for
_
,
value
:=
range
pointsBySeries
{
// columnValues := make([]interface{}, len(columnNames))
result
.
Series
=
append
(
result
.
Series
,
value
)
//
// err = rows.ScanSlice(&columnValues)
// if err != nil {
// result.Error = err
// return result
// }
//
// // bytes -> string
// for i := range columnValues {
// rowType := reflect.TypeOf(columnValues[i])
// e.log.Info("row", "type", rowType)
//
// rawValue := reflect.Indirect(reflect.ValueOf(columnValues[i]))
//
// // if rawValue is null then ignore
// if rawValue.Interface() == nil {
// continue
// }
//
// rawValueType := reflect.TypeOf(rawValue.Interface())
// vv := reflect.ValueOf(rawValue.Interface())
// e.log.Info("column type", "name", columnNames[i], "type", rawValueType, "vv", vv)
// }
// }
}
}
return
result
return
result
}
}
type
stringStringScan
struct
{
type
stringStringScan
struct
{
// cp are the column pointers
rowPtrs
[]
interface
{}
cp
[]
interface
{}
rowValues
[]
string
// row contains the final result
columnNames
[]
string
row
[]
string
columnCount
int
colCount
int
colNames
[]
string
time
null
.
Float
value
null
.
Float
metric
string
}
}
func
NewStringStringScan
(
columnNames
[]
string
)
*
stringStringScan
{
func
NewStringStringScan
(
columnNames
[]
string
)
*
stringStringScan
{
lenCN
:=
len
(
columnNames
)
s
:=
&
stringStringScan
{
s
:=
&
stringStringScan
{
c
p
:
make
([]
interface
{},
lenCN
),
c
olumnCount
:
len
(
columnNames
),
row
:
make
([]
string
,
lenCN
*
2
)
,
columnNames
:
columnNames
,
colCount
:
lenCN
,
rowPtrs
:
make
([]
interface
{},
len
(
columnNames
))
,
colNames
:
columnNames
,
rowValues
:
make
([]
string
,
len
(
columnNames
))
,
}
}
j
:=
0
for
i
:=
0
;
i
<
lenCN
;
i
++
{
for
i
:=
0
;
i
<
s
.
columnCount
;
i
++
{
s
.
cp
[
i
]
=
new
(
sql
.
RawBytes
)
s
.
rowPtrs
[
i
]
=
new
(
sql
.
RawBytes
)
s
.
row
[
j
]
=
s
.
colNames
[
i
]
j
=
j
+
2
}
}
return
s
return
s
}
}
func
(
s
*
stringStringScan
)
Update
(
rows
*
sql
.
Rows
)
error
{
func
(
s
*
stringStringScan
)
Update
(
rows
*
sql
.
Rows
)
error
{
if
err
:=
rows
.
Scan
(
s
.
cp
...
);
err
!=
nil
{
if
err
:=
rows
.
Scan
(
s
.
rowPtrs
...
);
err
!=
nil
{
return
err
return
err
}
}
j
:=
0
for
i
:=
0
;
i
<
s
.
colCount
;
i
++
{
for
i
:=
0
;
i
<
s
.
columnCount
;
i
++
{
if
rb
,
ok
:=
s
.
cp
[
i
]
.
(
*
sql
.
RawBytes
);
ok
{
if
rb
,
ok
:=
s
.
rowPtrs
[
i
]
.
(
*
sql
.
RawBytes
);
ok
{
s
.
row
[
j
+
1
]
=
string
(
*
rb
)
s
.
rowValues
[
i
]
=
string
(
*
rb
)
fmt
.
Printf
(
"column %s = %s"
,
s
.
columnNames
[
i
],
s
.
rowValues
[
i
])
switch
s
.
columnNames
[
i
]
{
case
"time_sec"
:
if
sec
,
err
:=
strconv
.
ParseInt
(
s
.
rowValues
[
i
],
10
,
64
);
err
==
nil
{
s
.
time
=
null
.
FloatFrom
(
float64
(
sec
*
1000
))
}
case
"value"
:
if
value
,
err
:=
strconv
.
ParseFloat
(
s
.
rowValues
[
i
],
64
);
err
==
nil
{
s
.
value
=
null
.
FloatFrom
(
value
)
}
case
"metric"
:
if
value
,
err
:=
strconv
.
ParseFloat
(
s
.
rowValues
[
i
],
64
);
err
==
nil
{
s
.
value
=
null
.
FloatFrom
(
value
)
}
}
*
rb
=
nil
// reset pointer to discard current value to avoid a bug
*
rb
=
nil
// reset pointer to discard current value to avoid a bug
}
else
{
}
else
{
return
fmt
.
Errorf
(
"Cannot convert index %d column %s to type *sql.RawBytes"
,
i
,
s
.
colNames
[
i
])
return
fmt
.
Errorf
(
"Cannot convert index %d column %s to type *sql.RawBytes"
,
i
,
s
.
col
umn
Names
[
i
])
}
}
j
=
j
+
2
}
}
return
nil
return
nil
}
}
func
(
s
*
stringStringScan
)
Get
()
[]
string
{
return
s
.
row
}
// type sqlDataRequest struct {
// Query string `json:"query"`
// Body []byte `json:"-"`
// }
//
// type seriesStruct struct {
// Columns []string `json:"columns"`
// Name string `json:"name"`
// Values [][]interface{} `json:"values"`
// }
//
// type resultsStruct struct {
// Series []seriesStruct `json:"series"`
// }
//
// type dataStruct struct {
// Results []resultsStruct `json:"results"`
// }
pkg/tsdb/request.go
View file @
d6d2080f
package
tsdb
package
tsdb
import
"context"
import
(
"context"
)
type
HandleRequestFunc
func
(
ctx
context
.
Context
,
req
*
Request
)
(
*
Response
,
error
)
type
HandleRequestFunc
func
(
ctx
context
.
Context
,
req
*
Request
)
(
*
Response
,
error
)
...
...
public/app/plugins/datasource/mysql/datasource.ts
View file @
d6d2080f
...
@@ -39,6 +39,11 @@ export class MysqlDatasource {
...
@@ -39,6 +39,11 @@ export class MysqlDatasource {
var
data
=
[];
var
data
=
[];
if
(
res
.
results
)
{
if
(
res
.
results
)
{
_
.
forEach
(
res
.
results
,
queryRes
=>
{
_
.
forEach
(
res
.
results
,
queryRes
=>
{
if
(
queryRes
.
error
)
{
throw
{
error
:
queryRes
.
error
,
message
:
queryRes
.
error
};
}
for
(
let
series
of
queryRes
.
series
)
{
for
(
let
series
of
queryRes
.
series
)
{
data
.
push
({
data
.
push
({
target
:
series
.
name
,
target
:
series
.
name
,
...
...
public/app/plugins/datasource/mysql/module.ts
View file @
d6d2080f
...
@@ -6,6 +6,21 @@ import {QueryCtrl} from 'app/plugins/sdk';
...
@@ -6,6 +6,21 @@ import {QueryCtrl} from 'app/plugins/sdk';
class
MysqlQueryCtrl
extends
QueryCtrl
{
class
MysqlQueryCtrl
extends
QueryCtrl
{
static
templateUrl
=
'partials/query.editor.html'
;
static
templateUrl
=
'partials/query.editor.html'
;
resultFormats
:
any
;
target
:
any
;
constructor
(
$scope
,
$injector
)
{
super
(
$scope
,
$injector
);
this
.
target
.
resultFormat
=
'time_series'
;
this
.
target
.
alias
=
"{{table}}{{col_3}}"
;
this
.
resultFormats
=
[
{
text
:
'Time series'
,
value
:
'time_series'
},
{
text
:
'Table'
,
value
:
'table'
},
];
}
}
}
class
MysqlConfigCtrl
{
class
MysqlConfigCtrl
{
...
...
public/app/plugins/datasource/mysql/partials/query.editor.html
View file @
d6d2080f
<query-editor-row
query-ctrl=
"ctrl"
can-collapse=
"false"
>
<query-editor-row
query-ctrl=
"ctrl"
can-collapse=
"false"
>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form gf-form--grow"
>
<div
class=
"gf-form gf-form--grow"
>
<textarea
rows=
"
3
"
class=
"gf-form-input"
ng-model=
"ctrl.target.rawSql"
spellcheck=
"false"
placeholder=
"query expression"
data-min-length=
0
data-items=
100
ng-model-onblur
ng-change=
"ctrl.refreshMetricData()"
></textarea>
<textarea
rows=
"
6
"
class=
"gf-form-input"
ng-model=
"ctrl.target.rawSql"
spellcheck=
"false"
placeholder=
"query expression"
data-min-length=
0
data-items=
100
ng-model-onblur
ng-change=
"ctrl.refreshMetricData()"
></textarea>
</div>
</div>
</div>
</div>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<label
class=
"gf-form-label query-keyword"
>
Format as
</label>
<div
class=
"gf-form-select-wrapper"
>
<select
class=
"gf-form-input gf-size-auto"
ng-model=
"ctrl.target.resultFormat"
ng-options=
"f.value as f.text for f in ctrl.resultFormats"
ng-change=
"ctrl.refresh()"
></select>
</div>
</div>
<div
class=
"gf-form max-width-30"
>
<label
class=
"gf-form-label query-keyword"
>
Name by
</label>
<input
type=
"text"
class=
"gf-form-input"
ng-model=
"ctrl.target.alias"
spellcheck=
'false'
placeholder=
"pattern"
ng-blur=
"ctrl.refresh()"
>
</div>
<div
class=
"gf-form gf-form--grow"
>
<div
class=
"gf-form-label gf-form-label--grow"
></div>
</div>
</div>
</query-editor-row>
</query-editor-row>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment