postgres.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package postgres
  2. import (
  3. "database/sql"
  4. "net/url"
  5. "strconv"
  6. "github.com/grafana/grafana/pkg/setting"
  7. "github.com/go-xorm/core"
  8. "github.com/grafana/grafana/pkg/infra/log"
  9. "github.com/grafana/grafana/pkg/models"
  10. "github.com/grafana/grafana/pkg/tsdb"
  11. "github.com/grafana/grafana/pkg/tsdb/sqleng"
  12. )
  13. func init() {
  14. tsdb.RegisterTsdbQueryEndpoint("postgres", newPostgresQueryEndpoint)
  15. }
  16. func newPostgresQueryEndpoint(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
  17. logger := log.New("tsdb.postgres")
  18. cnnstr := generateConnectionString(datasource)
  19. if setting.Env == setting.DEV {
  20. logger.Debug("getEngine", "connection", cnnstr)
  21. }
  22. config := sqleng.SqlQueryEndpointConfiguration{
  23. DriverName: "postgres",
  24. ConnectionString: cnnstr,
  25. Datasource: datasource,
  26. MetricColumnTypes: []string{"UNKNOWN", "TEXT", "VARCHAR", "CHAR"},
  27. }
  28. rowTransformer := postgresRowTransformer{
  29. log: logger,
  30. }
  31. timescaledb := datasource.JsonData.Get("timescaledb").MustBool(false)
  32. return sqleng.NewSqlQueryEndpoint(&config, &rowTransformer, newPostgresMacroEngine(timescaledb), logger)
  33. }
  34. func generateConnectionString(datasource *models.DataSource) string {
  35. sslmode := datasource.JsonData.Get("sslmode").MustString("verify-full")
  36. u := &url.URL{
  37. Scheme: "postgres",
  38. User: url.UserPassword(datasource.User, datasource.DecryptedPassword()),
  39. Host: datasource.Url, Path: datasource.Database,
  40. RawQuery: "sslmode=" + url.QueryEscape(sslmode),
  41. }
  42. return u.String()
  43. }
  44. type postgresRowTransformer struct {
  45. log log.Logger
  46. }
  47. func (t *postgresRowTransformer) Transform(columnTypes []*sql.ColumnType, rows *core.Rows) (tsdb.RowValues, error) {
  48. values := make([]interface{}, len(columnTypes))
  49. valuePtrs := make([]interface{}, len(columnTypes))
  50. for i := 0; i < len(columnTypes); i++ {
  51. valuePtrs[i] = &values[i]
  52. }
  53. if err := rows.Scan(valuePtrs...); err != nil {
  54. return nil, err
  55. }
  56. // convert types not handled by lib/pq
  57. // unhandled types are returned as []byte
  58. for i := 0; i < len(columnTypes); i++ {
  59. if value, ok := values[i].([]byte); ok {
  60. switch columnTypes[i].DatabaseTypeName() {
  61. case "NUMERIC":
  62. if v, err := strconv.ParseFloat(string(value), 64); err == nil {
  63. values[i] = v
  64. } else {
  65. t.log.Debug("Rows", "Error converting numeric to float", value)
  66. }
  67. case "UNKNOWN", "CIDR", "INET", "MACADDR":
  68. // char literals have type UNKNOWN
  69. values[i] = string(value)
  70. default:
  71. t.log.Debug("Rows", "Unknown database type", columnTypes[i].DatabaseTypeName(), "value", value)
  72. values[i] = string(value)
  73. }
  74. }
  75. }
  76. return values, nil
  77. }