mssql.go 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package mssql
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "github.com/grafana/grafana/pkg/setting"
  6. "net/url"
  7. "strconv"
  8. _ "github.com/denisenkom/go-mssqldb"
  9. "github.com/go-xorm/core"
  10. "github.com/grafana/grafana/pkg/infra/log"
  11. "github.com/grafana/grafana/pkg/models"
  12. "github.com/grafana/grafana/pkg/tsdb"
  13. "github.com/grafana/grafana/pkg/util"
  14. )
  15. func init() {
  16. tsdb.RegisterTsdbQueryEndpoint("mssql", newMssqlQueryEndpoint)
  17. }
  18. func newMssqlQueryEndpoint(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
  19. logger := log.New("tsdb.mssql")
  20. cnnstr := generateConnectionString(datasource)
  21. if setting.Env == setting.DEV {
  22. logger.Debug("getEngine", "connection", cnnstr)
  23. }
  24. config := tsdb.SqlQueryEndpointConfiguration{
  25. DriverName: "mssql",
  26. ConnectionString: cnnstr,
  27. Datasource: datasource,
  28. MetricColumnTypes: []string{"VARCHAR", "CHAR", "NVARCHAR", "NCHAR"},
  29. }
  30. rowTransformer := mssqlRowTransformer{
  31. log: logger,
  32. }
  33. return tsdb.NewSqlQueryEndpoint(&config, &rowTransformer, newMssqlMacroEngine(), logger)
  34. }
  35. func generateConnectionString(datasource *models.DataSource) string {
  36. server, port := util.SplitHostPortDefault(datasource.Url, "localhost", "1433")
  37. encrypt := datasource.JsonData.Get("encrypt").MustString("false")
  38. query := url.Values{}
  39. query.Add("database", datasource.Database)
  40. query.Add("encrypt", encrypt)
  41. u := &url.URL{
  42. Scheme: "sqlserver",
  43. User: url.UserPassword(datasource.User, datasource.DecryptedPassword()),
  44. Host: fmt.Sprintf("%s:%s", server, port),
  45. RawQuery: query.Encode(),
  46. }
  47. return u.String()
  48. }
  49. type mssqlRowTransformer struct {
  50. log log.Logger
  51. }
  52. func (t *mssqlRowTransformer) Transform(columnTypes []*sql.ColumnType, rows *core.Rows) (tsdb.RowValues, error) {
  53. values := make([]interface{}, len(columnTypes))
  54. valuePtrs := make([]interface{}, len(columnTypes))
  55. for i, stype := range columnTypes {
  56. t.log.Debug("type", "type", stype)
  57. valuePtrs[i] = &values[i]
  58. }
  59. if err := rows.Scan(valuePtrs...); err != nil {
  60. return nil, err
  61. }
  62. // convert types not handled by denisenkom/go-mssqldb
  63. // unhandled types are returned as []byte
  64. for i := 0; i < len(columnTypes); i++ {
  65. if value, ok := values[i].([]byte); ok {
  66. switch columnTypes[i].DatabaseTypeName() {
  67. case "MONEY", "SMALLMONEY", "DECIMAL":
  68. if v, err := strconv.ParseFloat(string(value), 64); err == nil {
  69. values[i] = v
  70. } else {
  71. t.log.Debug("Rows", "Error converting numeric to float", value)
  72. }
  73. default:
  74. t.log.Debug("Rows", "Unknown database type", columnTypes[i].DatabaseTypeName(), "value", value)
  75. values[i] = string(value)
  76. }
  77. }
  78. }
  79. return values, nil
  80. }