mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 00:01:48 +08:00
Chore: remove xorm from preference (#53803)
* Chore: remove xorm from preference * separte feature toggle * fix comments * fix comments * remove the dublicated namedexec
This commit is contained in:
@ -2,27 +2,29 @@ package pref
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"database/sql/driver"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrPrefNotFound = errors.New("preference not found")
|
var ErrPrefNotFound = errors.New("preference not found")
|
||||||
|
|
||||||
type Preference struct {
|
type Preference struct {
|
||||||
ID int64 `xorm:"pk autoincr 'id'"`
|
ID int64 `xorm:"pk autoincr 'id'" db:"id"`
|
||||||
OrgID int64 `xorm:"org_id"`
|
OrgID int64 `xorm:"org_id" db:"org_id"`
|
||||||
UserID int64 `xorm:"user_id"`
|
UserID int64 `xorm:"user_id" db:"user_id"`
|
||||||
TeamID int64 `xorm:"team_id"`
|
TeamID int64 `xorm:"team_id" db:"team_id"`
|
||||||
Teams []int64 `xorm:"extends"`
|
Teams []int64 `xorm:"extends"`
|
||||||
Version int
|
Version int `db:"version"`
|
||||||
HomeDashboardID int64 `xorm:"home_dashboard_id"`
|
HomeDashboardID int64 `xorm:"home_dashboard_id" db:"home_dashboard_id"`
|
||||||
Timezone string
|
Timezone string `db:"timezone"`
|
||||||
WeekStart string
|
WeekStart string `db:"week_start"`
|
||||||
Theme string
|
Theme string `db:"theme"`
|
||||||
Created time.Time
|
Created time.Time `db:"created"`
|
||||||
Updated time.Time
|
Updated time.Time `db:"updated"`
|
||||||
JSONData *PreferenceJSONData `xorm:"json_data"`
|
JSONData *PreferenceJSONData `xorm:"json_data" db:"json_data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetPreferenceWithDefaultsQuery struct {
|
type GetPreferenceWithDefaultsQuery struct {
|
||||||
@ -94,6 +96,27 @@ func (j *PreferenceJSONData) FromDB(data []byte) error {
|
|||||||
return dec.Decode(j)
|
return dec.Decode(j)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *PreferenceJSONData) Scan(val interface{}) error {
|
||||||
|
switch v := val.(type) {
|
||||||
|
case []byte:
|
||||||
|
if len(v) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return json.Unmarshal(v, &j)
|
||||||
|
case string:
|
||||||
|
if len(v) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return json.Unmarshal([]byte(v), &j)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported type: %T", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *PreferenceJSONData) Value() (driver.Value, error) {
|
||||||
|
return j.ToDB()
|
||||||
|
}
|
||||||
|
|
||||||
func (j *PreferenceJSONData) ToDB() ([]byte, error) {
|
func (j *PreferenceJSONData) ToDB() ([]byte, error) {
|
||||||
if j == nil {
|
if j == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -18,13 +18,20 @@ type Service struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(db db.DB, cfg *setting.Cfg, features *featuremgmt.FeatureManager) pref.Service {
|
func ProvideService(db db.DB, cfg *setting.Cfg, features *featuremgmt.FeatureManager) pref.Service {
|
||||||
return &Service{
|
service := &Service{
|
||||||
store: &sqlStore{
|
|
||||||
db: db,
|
|
||||||
},
|
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
features: features,
|
features: features,
|
||||||
}
|
}
|
||||||
|
if cfg.IsFeatureToggleEnabled("newDBLibrary") {
|
||||||
|
service.store = &sqlxStore{
|
||||||
|
sess: db.GetSqlxSession(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
service.store = &sqlStore{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) GetWithDefaults(ctx context.Context, query *pref.GetPreferenceWithDefaultsQuery) (*pref.Preference, error) {
|
func (s *Service) GetWithDefaults(ctx context.Context, query *pref.GetPreferenceWithDefaultsQuery) (*pref.Preference, error) {
|
||||||
|
68
pkg/services/preference/prefimpl/sqlx_store.go
Normal file
68
pkg/services/preference/prefimpl/sqlx_store.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package prefimpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sqlxStore struct {
|
||||||
|
sess *session.SessionDB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sqlxStore) Get(ctx context.Context, query *pref.Preference) (*pref.Preference, error) {
|
||||||
|
var prefs pref.Preference
|
||||||
|
err := s.sess.Get(ctx, &prefs, "SELECT * from preferences WHERE org_id=? AND user_id=? AND team_id=?", query.OrgID, query.UserID, query.TeamID)
|
||||||
|
if err != nil && errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, pref.ErrPrefNotFound
|
||||||
|
}
|
||||||
|
return &prefs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sqlxStore) List(ctx context.Context, query *pref.Preference) ([]*pref.Preference, error) {
|
||||||
|
prefs := make([]*pref.Preference, 0)
|
||||||
|
params := make([]interface{}, 0)
|
||||||
|
filter := ""
|
||||||
|
|
||||||
|
if len(query.Teams) > 0 {
|
||||||
|
filter = "(org_id=? AND team_id IN (?" + strings.Repeat(",?", len(query.Teams)-1) + ")) OR "
|
||||||
|
params = append(params, query.OrgID)
|
||||||
|
for _, v := range query.Teams {
|
||||||
|
params = append(params, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filter += "(org_id=? AND user_id=? AND team_id=0) OR (org_id=? AND team_id=0 AND user_id=0)"
|
||||||
|
params = append(params, query.OrgID)
|
||||||
|
params = append(params, query.UserID)
|
||||||
|
params = append(params, query.OrgID)
|
||||||
|
err := s.sess.Select(ctx, &prefs, fmt.Sprintf("SELECT * FROM preferences WHERE %s ORDER BY user_id ASC, team_id ASC", filter), params...)
|
||||||
|
return prefs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sqlxStore) Update(ctx context.Context, cmd *pref.Preference) error {
|
||||||
|
query := "UPDATE preferences SET org_id=:org_id, user_id=:user_id, team_id=:team_id, version=:version, home_dashboard_id=:home_dashboard_id, " +
|
||||||
|
"timezone=:timezone, week_start=:week_start, theme=:theme, created=:created, updated=:updated, json_data=:json_data WHERE id=:id"
|
||||||
|
_, err := s.sess.NamedExec(ctx, query, cmd)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sqlxStore) Insert(ctx context.Context, cmd *pref.Preference) (int64, error) {
|
||||||
|
var ID int64
|
||||||
|
query := "INSERT INTO preferences (org_id, user_id, team_id, version, home_dashboard_id, timezone, week_start, theme, created, updated, json_data) VALUES " +
|
||||||
|
"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
|
ID, err := s.sess.ExecWithReturningId(
|
||||||
|
ctx, query, cmd.OrgID, cmd.UserID, cmd.TeamID, cmd.Version, cmd.HomeDashboardID,
|
||||||
|
cmd.Timezone, cmd.WeekStart, cmd.Theme, cmd.Created, cmd.Updated, cmd.JSONData)
|
||||||
|
return ID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sqlxStore) DeleteByUser(ctx context.Context, userID int64) error {
|
||||||
|
_, err := s.sess.Exec(ctx, "DELETE FROM preferences WHERE user_id=?", userID)
|
||||||
|
return err
|
||||||
|
}
|
13
pkg/services/preference/prefimpl/sqlx_store_test.go
Normal file
13
pkg/services/preference/prefimpl/sqlx_store_test.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package prefimpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIntegrationSQLxPreferencesDataAccess(t *testing.T) {
|
||||||
|
testIntegrationPreferencesDataAccess(t, func(ss *sqlstore.SQLStore) store {
|
||||||
|
return &sqlxStore{sess: ss.GetSqlxSession()}
|
||||||
|
})
|
||||||
|
}
|
@ -2,11 +2,8 @@ package prefimpl
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
|
||||||
|
|
||||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type store interface {
|
type store interface {
|
||||||
@ -16,82 +13,3 @@ type store interface {
|
|||||||
Update(context.Context, *pref.Preference) error
|
Update(context.Context, *pref.Preference) error
|
||||||
DeleteByUser(context.Context, int64) error
|
DeleteByUser(context.Context, int64) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type sqlStore struct {
|
|
||||||
db db.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sqlStore) Get(ctx context.Context, query *pref.Preference) (*pref.Preference, error) {
|
|
||||||
var prefs pref.Preference
|
|
||||||
err := s.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
|
||||||
exist, err := sess.Where("org_id=? AND user_id=? AND team_id=?", query.OrgID, query.UserID, query.TeamID).Get(&prefs)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !exist {
|
|
||||||
return pref.ErrPrefNotFound
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &prefs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sqlStore) List(ctx context.Context, query *pref.Preference) ([]*pref.Preference, error) {
|
|
||||||
prefs := make([]*pref.Preference, 0)
|
|
||||||
params := make([]interface{}, 0)
|
|
||||||
filter := ""
|
|
||||||
|
|
||||||
if len(query.Teams) > 0 {
|
|
||||||
filter = "(org_id=? AND team_id IN (?" + strings.Repeat(",?", len(query.Teams)-1) + ")) OR "
|
|
||||||
params = append(params, query.OrgID)
|
|
||||||
for _, v := range query.Teams {
|
|
||||||
params = append(params, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filter += "(org_id=? AND user_id=? AND team_id=0) OR (org_id=? AND team_id=0 AND user_id=0)"
|
|
||||||
params = append(params, query.OrgID)
|
|
||||||
params = append(params, query.UserID)
|
|
||||||
params = append(params, query.OrgID)
|
|
||||||
|
|
||||||
err := s.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
|
||||||
err := dbSession.Where(filter, params...).
|
|
||||||
OrderBy("user_id ASC, team_id ASC").
|
|
||||||
Find(&prefs)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return prefs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sqlStore) Update(ctx context.Context, cmd *pref.Preference) error {
|
|
||||||
return s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
|
||||||
_, err := sess.ID(cmd.ID).AllCols().Update(cmd)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sqlStore) Insert(ctx context.Context, cmd *pref.Preference) (int64, error) {
|
|
||||||
var ID int64
|
|
||||||
var err error
|
|
||||||
err = s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
|
||||||
ID, err = sess.Insert(cmd)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return ID, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sqlStore) DeleteByUser(ctx context.Context, userID int64) error {
|
|
||||||
return s.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
|
||||||
var rawSQL = "DELETE FROM preferences WHERE user_id = ?"
|
|
||||||
_, err := dbSession.Exec(rawSQL, userID)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -12,12 +12,14 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIntegrationPreferencesDataAccess(t *testing.T) {
|
type getStore func(*sqlstore.SQLStore) store
|
||||||
|
|
||||||
|
func testIntegrationPreferencesDataAccess(t *testing.T, fn getStore) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skipping integration test")
|
t.Skip("skipping integration test")
|
||||||
}
|
}
|
||||||
ss := sqlstore.InitTestDB(t)
|
ss := sqlstore.InitTestDB(t)
|
||||||
prefStore := sqlStore{db: ss}
|
prefStore := fn(ss)
|
||||||
orgNavbarPreferences := pref.NavbarPreference{
|
orgNavbarPreferences := pref.NavbarPreference{
|
||||||
SavedItems: []pref.NavLink{{
|
SavedItems: []pref.NavLink{{
|
||||||
ID: "alerting",
|
ID: "alerting",
|
||||||
@ -115,7 +117,7 @@ func TestIntegrationPreferencesDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Update for a user should only modify a single value", func(t *testing.T) {
|
t.Run("Update for a user should only modify a single value", func(t *testing.T) {
|
||||||
ss := sqlstore.InitTestDB(t)
|
ss := sqlstore.InitTestDB(t)
|
||||||
prefStore := sqlStore{db: ss}
|
prefStore := fn(ss)
|
||||||
id, err := prefStore.Insert(context.Background(), &pref.Preference{
|
id, err := prefStore.Insert(context.Background(), &pref.Preference{
|
||||||
UserID: user.SignedInUser{}.UserID,
|
UserID: user.SignedInUser{}.UserID,
|
||||||
Theme: "dark",
|
Theme: "dark",
|
||||||
@ -167,4 +169,11 @@ func TestIntegrationPreferencesDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
t.Run("delete preference by user", func(t *testing.T) {
|
||||||
|
err := prefStore.DeleteByUser(context.Background(), user.SignedInUser{}.UserID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
query := &pref.Preference{OrgID: 0, UserID: user.SignedInUser{}.UserID, TeamID: 0}
|
||||||
|
_, err = prefStore.Get(context.Background(), query)
|
||||||
|
require.EqualError(t, err, pref.ErrPrefNotFound.Error())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
89
pkg/services/preference/prefimpl/xorm_store.go
Normal file
89
pkg/services/preference/prefimpl/xorm_store.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package prefimpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sqlStore struct {
|
||||||
|
db db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sqlStore) Get(ctx context.Context, query *pref.Preference) (*pref.Preference, error) {
|
||||||
|
var prefs pref.Preference
|
||||||
|
err := s.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||||
|
exist, err := sess.Where("org_id=? AND user_id=? AND team_id=?", query.OrgID, query.UserID, query.TeamID).Get(&prefs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exist {
|
||||||
|
return pref.ErrPrefNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &prefs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sqlStore) List(ctx context.Context, query *pref.Preference) ([]*pref.Preference, error) {
|
||||||
|
prefs := make([]*pref.Preference, 0)
|
||||||
|
params := make([]interface{}, 0)
|
||||||
|
filter := ""
|
||||||
|
|
||||||
|
if len(query.Teams) > 0 {
|
||||||
|
filter = "(org_id=? AND team_id IN (?" + strings.Repeat(",?", len(query.Teams)-1) + ")) OR "
|
||||||
|
params = append(params, query.OrgID)
|
||||||
|
for _, v := range query.Teams {
|
||||||
|
params = append(params, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filter += "(org_id=? AND user_id=? AND team_id=0) OR (org_id=? AND team_id=0 AND user_id=0)"
|
||||||
|
params = append(params, query.OrgID)
|
||||||
|
params = append(params, query.UserID)
|
||||||
|
params = append(params, query.OrgID)
|
||||||
|
|
||||||
|
err := s.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
||||||
|
err := dbSession.Where(filter, params...).
|
||||||
|
OrderBy("user_id ASC, team_id ASC").
|
||||||
|
Find(&prefs)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return prefs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sqlStore) Update(ctx context.Context, cmd *pref.Preference) error {
|
||||||
|
return s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||||
|
_, err := sess.ID(cmd.ID).AllCols().Update(cmd)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sqlStore) Insert(ctx context.Context, cmd *pref.Preference) (int64, error) {
|
||||||
|
var ID int64
|
||||||
|
var err error
|
||||||
|
err = s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||||
|
ID, err = sess.Insert(cmd)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return ID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sqlStore) DeleteByUser(ctx context.Context, userID int64) error {
|
||||||
|
return s.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
||||||
|
var rawSQL = "DELETE FROM preferences WHERE user_id = ?"
|
||||||
|
_, err := dbSession.Exec(rawSQL, userID)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
13
pkg/services/preference/prefimpl/xorm_store_test.go
Normal file
13
pkg/services/preference/prefimpl/xorm_store_test.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package prefimpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIntegrationXORMPreferencesDataAccess(t *testing.T) {
|
||||||
|
testIntegrationPreferencesDataAccess(t, func(ss *sqlstore.SQLStore) store {
|
||||||
|
return &sqlStore{db: ss}
|
||||||
|
})
|
||||||
|
}
|
@ -299,6 +299,10 @@ func (ss *SQLStore) buildConnectionString() (string, error) {
|
|||||||
cnnstr += "&sql_mode='ANSI_QUOTES'"
|
cnnstr += "&sql_mode='ANSI_QUOTES'"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ss.Cfg.IsFeatureToggleEnabled("newDBLibrary") {
|
||||||
|
cnnstr += "&parseTime=true"
|
||||||
|
}
|
||||||
|
|
||||||
cnnstr += ss.buildExtraConnectionString('&')
|
cnnstr += ss.buildExtraConnectionString('&')
|
||||||
case migrator.Postgres:
|
case migrator.Postgres:
|
||||||
addr, err := util.SplitHostPortDefault(ss.dbCfg.Host, "127.0.0.1", "5432")
|
addr, err := util.SplitHostPortDefault(ss.dbCfg.Host, "127.0.0.1", "5432")
|
||||||
|
@ -28,7 +28,7 @@ func MySQLTestDB() TestDB {
|
|||||||
if port == "" {
|
if port == "" {
|
||||||
port = "3306"
|
port = "3306"
|
||||||
}
|
}
|
||||||
conn_str := fmt.Sprintf("grafana:password@tcp(%s:%s)/grafana_tests?collation=utf8mb4_unicode_ci&sql_mode='ANSI_QUOTES'", host, port)
|
conn_str := fmt.Sprintf("grafana:password@tcp(%s:%s)/grafana_tests?collation=utf8mb4_unicode_ci&sql_mode='ANSI_QUOTES'&parseTime=true", host, port)
|
||||||
return TestDB{
|
return TestDB{
|
||||||
DriverName: "mysql",
|
DriverName: "mysql",
|
||||||
ConnStr: conn_str,
|
ConnStr: conn_str,
|
||||||
|
Reference in New Issue
Block a user