Commit afb847ac by Torkel Ödegaard

a lot of work on database schema and migration setup, postgres now works, every…

a lot of work on database schema and migration setup, postgres now works, every integration test passes for all database types, only token table left to do
parent 8bb9126b
...@@ -25,15 +25,17 @@ type DataSource struct { ...@@ -25,15 +25,17 @@ type DataSource struct {
Id int64 Id int64
AccountId int64 AccountId int64
Name string Name string
Type DsType Type DsType
Access DsAccess Access DsAccess
Url string Url string
Password string Password string
User string User string
Database string Database string
BasicAuth bool BasicAuth bool
IsDefault bool BasicAuthUser string
BasicAuthPassword string
IsDefault bool
Created time.Time Created time.Time
Updated time.Time Updated time.Time
......
...@@ -81,7 +81,7 @@ func TestAccountDataAccess(t *testing.T) { ...@@ -81,7 +81,7 @@ func TestAccountDataAccess(t *testing.T) {
err := SetUsingAccount(&cmd) err := SetUsingAccount(&cmd)
So(err, ShouldBeNil) So(err, ShouldBeNil)
Convey("Logged in user query should return correct using account info", func() { Convey("SignedInUserQuery with a different account", func() {
query := m.GetSignedInUserQuery{UserId: ac2.Id} query := m.GetSignedInUserQuery{UserId: ac2.Id}
err := GetSignedInUser(&query) err := GetSignedInUser(&query)
......
...@@ -44,11 +44,11 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error { ...@@ -44,11 +44,11 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
// insert new tags // insert new tags
tags := dash.GetTags() tags := dash.GetTags()
if len(tags) > 0 { if len(tags) > 0 {
tagRows := make([]DashboardTag, len(tags))
for _, tag := range tags { for _, tag := range tags {
tagRows = append(tagRows, DashboardTag{Term: tag, DashboardId: dash.Id}) if _, err := sess.Insert(&DashboardTag{DashboardId: dash.Id, Term: tag}); err != nil {
return err
}
} }
sess.InsertMulti(&tagRows)
} }
cmd.Result = dash cmd.Result = dash
...@@ -120,8 +120,7 @@ func SearchDashboards(query *m.SearchDashboardsQuery) error { ...@@ -120,8 +120,7 @@ func SearchDashboards(query *m.SearchDashboardsQuery) error {
} }
func GetDashboardTags(query *m.GetDashboardTagsQuery) error { func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
sess := x.Sql("select count() as count, term from dashboard_tag group by term") sess := x.Sql("select count(*) as count, term from dashboard_tag group by term")
err := sess.Find(&query.Result) err := sess.Find(&query.Result)
return err return err
} }
......
...@@ -95,7 +95,7 @@ func TestDashboardDataAccess(t *testing.T) { ...@@ -95,7 +95,7 @@ func TestDashboardDataAccess(t *testing.T) {
err := GetDashboardTags(&query) err := GetDashboardTags(&query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 3) So(len(query.Result), ShouldEqual, 2)
}) })
}) })
}) })
......
...@@ -8,16 +8,24 @@ import ( ...@@ -8,16 +8,24 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
m "github.com/torkelo/grafana-pro/pkg/models" m "github.com/torkelo/grafana-pro/pkg/models"
"github.com/torkelo/grafana-pro/pkg/services/sqlstore/sqlutil"
) )
func InitTestDB(t *testing.T) { func InitTestDB(t *testing.T) {
x, err := xorm.NewEngine("sqlite3", ":memory:")
x, err := xorm.NewEngine(sqlutil.TestDB_Sqlite3.DriverName, sqlutil.TestDB_Sqlite3.ConnStr)
//x, err := xorm.NewEngine(sqlutil.TestDB_Mysql.DriverName, sqlutil.TestDB_Mysql.ConnStr)
//x, err := xorm.NewEngine(sqlutil.TestDB_Postgres.DriverName, sqlutil.TestDB_Postgres.ConnStr)
if err != nil { if err != nil {
t.Fatalf("Failed to init in memory sqllite3 db %v", err) t.Fatalf("Failed to init in memory sqllite3 db %v", err)
} }
SetEngine(x, false) sqlutil.CleanDB(x)
if err := SetEngine(x, false); err != nil {
t.Fatal(err)
}
} }
type Test struct { type Test struct {
......
package migrations package sqlstore
import "time" import . "github.com/torkelo/grafana-pro/pkg/services/sqlstore/migrator"
func AddMigrations(mg *Migrator) { func addMigrations(mg *Migrator) {
addMigrationLogMigrations(mg) addMigrationLogMigrations(mg)
addUserMigrations(mg) addUserMigrations(mg)
addAccountMigrations(mg) addAccountMigrations(mg)
addDashboardMigration(mg) addDashboardMigration(mg)
addDataSourceMigration(mg)
} }
func addMigrationLogMigrations(mg *Migrator) { func addMigrationLogMigrations(mg *Migrator) {
...@@ -38,10 +39,10 @@ func addUserMigrations(mg *Migrator) { ...@@ -38,10 +39,10 @@ func addUserMigrations(mg *Migrator) {
)) ))
//------- user table indexes ------------------ //------- user table indexes ------------------
mg.AddMigration("add unique index UIX_user.login", new(AddIndexMigration). mg.AddMigration("add unique index user.login", new(AddIndexMigration).
Name("UIX_user_login").Table("user").Columns("login")) Table("user").Columns("login").Unique())
mg.AddMigration("add unique index UIX_user.email", new(AddIndexMigration). mg.AddMigration("add unique index user.email", new(AddIndexMigration).
Name("UIX_user_email").Table("user").Columns("email")) Table("user").Columns("email").Unique())
} }
func addAccountMigrations(mg *Migrator) { func addAccountMigrations(mg *Migrator) {
...@@ -53,8 +54,8 @@ func addAccountMigrations(mg *Migrator) { ...@@ -53,8 +54,8 @@ func addAccountMigrations(mg *Migrator) {
&Column{Name: "updated", Type: DB_DateTime, Nullable: false}, &Column{Name: "updated", Type: DB_DateTime, Nullable: false},
)) ))
mg.AddMigration("add unique index UIX_account.name", new(AddIndexMigration). mg.AddMigration("add unique index account.name", new(AddIndexMigration).
Name("UIX_account_name").Table("account").Columns("name")) Table("account").Columns("name").Unique())
//------- account_user table ------------------- //------- account_user table -------------------
mg.AddMigration("create account_user table", new(AddTableMigration). mg.AddMigration("create account_user table", new(AddTableMigration).
...@@ -67,20 +68,8 @@ func addAccountMigrations(mg *Migrator) { ...@@ -67,20 +68,8 @@ func addAccountMigrations(mg *Migrator) {
&Column{Name: "updated", Type: DB_DateTime}, &Column{Name: "updated", Type: DB_DateTime},
)) ))
mg.AddMigration("add unique index UIX_account_user", new(AddIndexMigration). mg.AddMigration("add unique index account_user_aid_uid", new(AddIndexMigration).
Name("UIX_account_user").Table("account_user").Columns("account_id", "user_id")) Name("aid_uid").Table("account_user").Columns("account_id", "user_id").Unique())
}
type Dashboard struct {
Id int64
Slug string `xorm:"index(IX_AccountIdSlug)"`
AccountId int64 `xorm:"index(IX_AccountIdSlug)"`
Created time.Time
Updated time.Time
Title string
Data map[string]interface{}
} }
func addDashboardMigration(mg *Migrator) { func addDashboardMigration(mg *Migrator) {
...@@ -95,10 +84,48 @@ func addDashboardMigration(mg *Migrator) { ...@@ -95,10 +84,48 @@ func addDashboardMigration(mg *Migrator) {
&Column{Name: "updated", Type: DB_DateTime, Nullable: false}, &Column{Name: "updated", Type: DB_DateTime, Nullable: false},
)) ))
mg.AddMigration("create dashboard_tag table", new(AddTableMigration).
Name("dashboard_tag").WithColumns(
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
&Column{Name: "dashboard_id", Type: DB_BigInt, Nullable: false},
&Column{Name: "term", Type: DB_NVarchar, Length: 50, Nullable: false},
))
//------- indexes ------------------
mg.AddMigration("add index dashboard.account_id", new(AddIndexMigration).
Table("dashboard").Columns("account_id"))
mg.AddMigration("add unique index dashboard_account_id_slug", new(AddIndexMigration).
Table("dashboard").Columns("account_id", "slug").Unique())
mg.AddMigration("add unique index dashboard_tag.dasboard_id_term", new(AddIndexMigration).
Table("dashboard_tag").Columns("dashboard_id", "term").Unique())
}
func addDataSourceMigration(mg *Migrator) {
mg.AddMigration("create data_source table", new(AddTableMigration).
Name("data_source").WithColumns(
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
&Column{Name: "account_id", Type: DB_BigInt, Nullable: false},
&Column{Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
&Column{Name: "access", Type: DB_NVarchar, Length: 255, Nullable: false},
&Column{Name: "url", Type: DB_NVarchar, Length: 255, Nullable: false},
&Column{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
&Column{Name: "user", Type: DB_NVarchar, Length: 255, Nullable: true},
&Column{Name: "database", Type: DB_NVarchar, Length: 255, Nullable: true},
&Column{Name: "basic_auth", Type: DB_Bool, Nullable: false},
&Column{Name: "basic_auth_user", Type: DB_NVarchar, Length: 255, Nullable: true},
&Column{Name: "basic_auth_password", Type: DB_NVarchar, Length: 255, Nullable: true},
&Column{Name: "is_default", Type: DB_Bool, Nullable: false},
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
))
//------- indexes ------------------ //------- indexes ------------------
mg.AddMigration("add unique index UIX_dashboard.account_id", new(AddIndexMigration). mg.AddMigration("add index data_source.account_id", new(AddIndexMigration).
Name("UIX_dashboard_account_id").Table("dashboard").Columns("account_id")) Table("data_source").Columns("account_id"))
mg.AddMigration("add unique index UIX_dashboard_account_id_slug", new(AddIndexMigration). mg.AddMigration("add unique index data_source.account_id_name", new(AddIndexMigration).
Name("UIX_dashboard_account_id_slug").Table("dashboard").Columns("account_id", "slug")) Table("data_source").Columns("account_id", "name").Unique())
} }
package migrations package sqlstore
import ( import (
"fmt" "fmt"
...@@ -7,52 +7,34 @@ import ( ...@@ -7,52 +7,34 @@ import (
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
"github.com/torkelo/grafana-pro/pkg/log" "github.com/torkelo/grafana-pro/pkg/log"
. "github.com/torkelo/grafana-pro/pkg/services/sqlstore/migrator"
"github.com/torkelo/grafana-pro/pkg/services/sqlstore/sqlutil"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
) )
func cleanDB(x *xorm.Engine) { var indexTypes = []string{"Unknown", "INDEX", "UNIQUE INDEX"}
tables, _ := x.DBMetas()
sess := x.NewSession()
defer sess.Close()
for _, table := range tables { func ATestMigrations(t *testing.T) {
if _, err := sess.Exec("SET FOREIGN_KEY_CHECKS = 0"); err != nil {
panic("Failed to disable foreign key checks")
}
if _, err := sess.Exec("DROP TABLE " + table.Name); err != nil {
panic(fmt.Sprintf("Failed to delete table: %v, err: %v", table.Name, err))
}
if _, err := sess.Exec("SET FOREIGN_KEY_CHECKS = 1"); err != nil {
panic("Failed to disable foreign key checks")
}
}
}
var indexTypes = []string{"Unknown", "", "UNIQUE"}
func TestMigrations(t *testing.T) {
log.NewLogger(0, "console", `{"level": 0}`) log.NewLogger(0, "console", `{"level": 0}`)
testDBs := [][]string{ testDBs := []sqlutil.TestDB{
[]string{"postgres", "user=grafanatest password=grafanatest host=localhost port=5432 dbname=grafanatest sslmode=disable"}, sqlutil.TestDB_Sqlite3,
[]string{"mysql", "grafana:password@tcp(localhost:3306)/grafana_tests?charset=utf8"}, sqlutil.TestDB_Mysql,
[]string{"sqlite3", ":memory:"}, sqlutil.TestDB_Postgres,
} }
for _, testDB := range testDBs { for _, testDB := range testDBs {
Convey("Initial "+testDB[0]+" migration", t, func() { Convey("Initial "+testDB.DriverName+" migration", t, func() {
x, err := xorm.NewEngine(testDB[0], testDB[1]) x, err := xorm.NewEngine(testDB.DriverName, testDB.ConnStr)
So(err, ShouldBeNil) So(err, ShouldBeNil)
if testDB[0] == "mysql" { sqlutil.CleanDB(x)
cleanDB(x)
}
mg := NewMigrator(x) mg := NewMigrator(x)
mg.LogLevel = log.DEBUG mg.LogLevel = log.DEBUG
AddMigrations(mg) addMigrations(mg)
err = mg.Start() err = mg.Start()
So(err, ShouldBeNil) So(err, ShouldBeNil)
......
package migrations package migrator
import (
"fmt"
"strings"
)
type MigrationBase struct { type MigrationBase struct {
id string id string
...@@ -87,6 +92,9 @@ func (m *AddIndexMigration) Columns(columns ...string) *AddIndexMigration { ...@@ -87,6 +92,9 @@ func (m *AddIndexMigration) Columns(columns ...string) *AddIndexMigration {
} }
func (m *AddIndexMigration) Sql(dialect Dialect) string { func (m *AddIndexMigration) Sql(dialect Dialect) string {
if m.index.Name == "" {
m.index.Name = fmt.Sprintf("%s", strings.Join(m.index.Cols, "_"))
}
return dialect.CreateIndexSql(m.tableName, &m.index) return dialect.CreateIndexSql(m.tableName, &m.index)
} }
......
package migrations package migrator
// Notice // Notice
// code based on parts from from https://github.com/go-xorm/core/blob/3e0fa232ab5c90996406c0cd7ae86ad0e5ecf85f/column.go // code based on parts from from https://github.com/go-xorm/core/blob/3e0fa232ab5c90996406c0cd7ae86ad0e5ecf85f/column.go
......
package migrations package migrator
import ( import (
"fmt" "fmt"
...@@ -23,6 +23,19 @@ type Dialect interface { ...@@ -23,6 +23,19 @@ type Dialect interface {
TableCheckSql(tableName string) (string, []interface{}) TableCheckSql(tableName string) (string, []interface{})
} }
func NewDialect(name string) Dialect {
switch name {
case MYSQL:
return NewMysqlDialect()
case SQLITE:
return NewSqlite3Dialect()
case POSTGRES:
return NewPostgresDialect()
}
panic("Unsupported database type: " + name)
}
type BaseDialect struct { type BaseDialect struct {
dialect Dialect dialect Dialect
driverName string driverName string
......
package migrations package migrator
import ( import (
"time" "time"
...@@ -32,16 +32,7 @@ func NewMigrator(engine *xorm.Engine) *Migrator { ...@@ -32,16 +32,7 @@ func NewMigrator(engine *xorm.Engine) *Migrator {
mg.x = engine mg.x = engine
mg.LogLevel = log.WARN mg.LogLevel = log.WARN
mg.migrations = make([]Migration, 0) mg.migrations = make([]Migration, 0)
mg.dialect = NewDialect(mg.x.DriverName())
switch mg.x.DriverName() {
case MYSQL:
mg.dialect = NewMysqlDialect()
case SQLITE:
mg.dialect = NewSqlite3Dialect()
case POSTGRES:
mg.dialect = NewPostgresDialect()
}
return mg return mg
} }
......
package migrations package migrator
import ( import "strconv"
"strconv"
"github.com/go-xorm/core"
)
type Postgres struct { type Postgres struct {
BaseDialect BaseDialect
...@@ -35,16 +31,16 @@ func (db *Postgres) SqlType(c *Column) string { ...@@ -35,16 +31,16 @@ func (db *Postgres) SqlType(c *Column) string {
case DB_TinyInt: case DB_TinyInt:
res = DB_SmallInt res = DB_SmallInt
return res return res
case DB_MediumInt, core.Int, core.Integer: case DB_MediumInt, DB_Int, DB_Integer:
if c.IsAutoIncrement { if c.IsAutoIncrement {
return DB_Serial return DB_Serial
} }
return DB_Integer return DB_Integer
case DB_Serial, core.BigSerial: case DB_Serial, DB_BigSerial:
c.IsAutoIncrement = true c.IsAutoIncrement = true
c.Nullable = false c.Nullable = false
res = t res = t
case DB_Binary, core.VarBinary: case DB_Binary, DB_VarBinary:
return DB_Bytea return DB_Bytea
case DB_DateTime: case DB_DateTime:
res = DB_TimeStamp res = DB_TimeStamp
...@@ -52,13 +48,13 @@ func (db *Postgres) SqlType(c *Column) string { ...@@ -52,13 +48,13 @@ func (db *Postgres) SqlType(c *Column) string {
return "timestamp with time zone" return "timestamp with time zone"
case DB_Float: case DB_Float:
res = DB_Real res = DB_Real
case DB_TinyText, core.MediumText, core.LongText: case DB_TinyText, DB_MediumText, DB_LongText:
res = DB_Text res = DB_Text
case DB_NVarchar: case DB_NVarchar:
res = DB_Varchar res = DB_Varchar
case DB_Uuid: case DB_Uuid:
res = DB_Uuid res = DB_Uuid
case DB_Blob, core.TinyBlob, core.MediumBlob, core.LongBlob: case DB_Blob, DB_TinyBlob, DB_MediumBlob, DB_LongBlob:
return DB_Bytea return DB_Bytea
case DB_Double: case DB_Double:
return "DOUBLE PRECISION" return "DOUBLE PRECISION"
......
package migrations package migrator
import "github.com/go-xorm/core"
type Sqlite3 struct { type Sqlite3 struct {
BaseDialect BaseDialect
...@@ -32,7 +30,7 @@ func (db *Sqlite3) SqlType(c *Column) string { ...@@ -32,7 +30,7 @@ func (db *Sqlite3) SqlType(c *Column) string {
case DB_TimeStampz: case DB_TimeStampz:
return DB_Text return DB_Text
case DB_Char, DB_Varchar, DB_NVarchar, DB_TinyText, DB_Text, DB_MediumText, DB_LongText: case DB_Char, DB_Varchar, DB_NVarchar, DB_TinyText, DB_Text, DB_MediumText, DB_LongText:
return core.Text return DB_Text
case DB_Bit, DB_TinyInt, DB_SmallInt, DB_MediumInt, DB_Int, DB_Integer, DB_BigInt, DB_Bool: case DB_Bit, DB_TinyInt, DB_SmallInt, DB_MediumInt, DB_Int, DB_Integer, DB_BigInt, DB_Bool:
return DB_Integer return DB_Integer
case DB_Float, DB_Double, DB_Real: case DB_Float, DB_Double, DB_Real:
...@@ -45,7 +43,7 @@ func (db *Sqlite3) SqlType(c *Column) string { ...@@ -45,7 +43,7 @@ func (db *Sqlite3) SqlType(c *Column) string {
c.IsPrimaryKey = true c.IsPrimaryKey = true
c.IsAutoIncrement = true c.IsAutoIncrement = true
c.Nullable = false c.Nullable = false
return core.Integer return DB_Integer
default: default:
return c.Type return c.Type
} }
......
package migrations package migrator
const ( const (
POSTGRES = "postgres" POSTGRES = "postgres"
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
"github.com/torkelo/grafana-pro/pkg/bus" "github.com/torkelo/grafana-pro/pkg/bus"
"github.com/torkelo/grafana-pro/pkg/log" "github.com/torkelo/grafana-pro/pkg/log"
m "github.com/torkelo/grafana-pro/pkg/models" m "github.com/torkelo/grafana-pro/pkg/models"
"github.com/torkelo/grafana-pro/pkg/services/sqlstore/migrations" "github.com/torkelo/grafana-pro/pkg/services/sqlstore/migrator"
"github.com/torkelo/grafana-pro/pkg/setting" "github.com/torkelo/grafana-pro/pkg/setting"
"github.com/torkelo/grafana-pro/pkg/util" "github.com/torkelo/grafana-pro/pkg/util"
...@@ -20,8 +20,9 @@ import ( ...@@ -20,8 +20,9 @@ import (
) )
var ( var (
x *xorm.Engine x *xorm.Engine
tables []interface{} dialect migrator.Dialect
tables []interface{}
HasEngine bool HasEngine bool
...@@ -34,9 +35,7 @@ var ( ...@@ -34,9 +35,7 @@ var (
func init() { func init() {
tables = make([]interface{}, 0) tables = make([]interface{}, 0)
tables = append(tables, new(m.Token))
tables = append(tables, new(m.DataSource), new(DashboardTag),
new(m.Token))
} }
func EnsureAdminUser() { func EnsureAdminUser() {
...@@ -76,9 +75,10 @@ func NewEngine() { ...@@ -76,9 +75,10 @@ func NewEngine() {
func SetEngine(engine *xorm.Engine, enableLog bool) (err error) { func SetEngine(engine *xorm.Engine, enableLog bool) (err error) {
x = engine x = engine
dialect = migrator.NewDialect(x.DriverName())
migrator := migrations.NewMigrator(x) migrator := migrator.NewMigrator(x)
migrations.AddMigrations(migrator) addMigrations(migrator)
if err := migrator.Start(); err != nil { if err := migrator.Start(); err != nil {
return fmt.Errorf("Sqlstore::Migration failed err: %v\n", err) return fmt.Errorf("Sqlstore::Migration failed err: %v\n", err)
......
package sqlutil
import (
"fmt"
"github.com/go-xorm/xorm"
)
type TestDB struct {
DriverName string
ConnStr string
}
var TestDB_Sqlite3 = TestDB{DriverName: "sqlite3", ConnStr: ":memory:"}
var TestDB_Mysql = TestDB{DriverName: "mysql", ConnStr: "grafana:password@tcp(localhost:3306)/grafana_tests?charset=utf8"}
var TestDB_Postgres = TestDB{DriverName: "postgres", ConnStr: "user=grafanatest password=grafanatest host=localhost port=5432 dbname=grafanatest sslmode=disable"}
func CleanDB(x *xorm.Engine) {
if x.DriverName() == "postgres" {
sess := x.NewSession()
defer sess.Close()
if _, err := sess.Exec("DROP SCHEMA public CASCADE;"); err != nil {
panic("Failed to drop schema public")
}
if _, err := sess.Exec("CREATE SCHEMA public;"); err != nil {
panic("Failed to create schema public")
}
} else if x.DriverName() == "mysql" {
tables, _ := x.DBMetas()
sess := x.NewSession()
defer sess.Close()
for _, table := range tables {
if _, err := sess.Exec("set foreign_key_checks = 0"); err != nil {
panic("failed to disable foreign key checks")
}
if _, err := sess.Exec("drop table " + table.Name + " ;"); err != nil {
panic(fmt.Sprintf("failed to delete table: %v, err: %v", table.Name, err))
}
if _, err := sess.Exec("set foreign_key_checks = 1"); err != nil {
panic("failed to disable foreign key checks")
}
}
}
}
...@@ -150,18 +150,18 @@ func GetUserAccounts(query *m.GetUserAccountsQuery) error { ...@@ -150,18 +150,18 @@ func GetUserAccounts(query *m.GetUserAccountsQuery) error {
func GetSignedInUser(query *m.GetSignedInUserQuery) error { func GetSignedInUser(query *m.GetSignedInUserQuery) error {
var rawSql = `SELECT var rawSql = `SELECT
user.id as user_id, u.id as user_id,
user.is_admin as is_grafana_admin, u.is_admin as is_grafana_admin,
user.email as email, u.email as email,
user.login as login, u.login as login,
user.name as name, u.name as name,
account.name as account_name, account.name as account_name,
account_user.role as account_role, account_user.role as account_role,
account.id as account_id account.id as account_id
FROM user FROM ` + dialect.Quote("user") + ` as u
LEFT OUTER JOIN account_user on account_user.account_id = user.account_id and account_user.user_id = user.id LEFT OUTER JOIN account_user on account_user.account_id = u.account_id and account_user.user_id = u.id
LEFT OUTER JOIN account on account.id = user.account_id LEFT OUTER JOIN account on account.id = u.account_id
WHERE user.id=?` WHERE u.id=?`
var user m.SignedInUser var user m.SignedInUser
sess := x.Table("user") sess := x.Table("user")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment