mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Unified Storage: create kind_version table migration, add SQL and fix db (#87977)
* fix database interfaces * add queries * fix queries * fix linters * add owner to imported go library * remove unused funcs * run go work sync * improve critical section fix in data race fix * fix linters * remove sync * fix typo * improve data embedding * fix linters * fix migration * remove unnecessary comments * fix linters * improve SQL templates readability * remove group_version from kind_version for consistency in History method * add created_at and updated_at columns to kind_version table
This commit is contained in:
parent
6744dae806
commit
8b02b6b76a
1
go.mod
1
go.mod
@ -224,6 +224,7 @@ require (
|
|||||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
|
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
|
||||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.2 // @grafana/grafana-search-and-storage
|
||||||
github.com/FZambia/eagle v0.1.0 // indirect
|
github.com/FZambia/eagle v0.1.0 // indirect
|
||||||
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c // indirect
|
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c // indirect
|
||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
|
3
go.sum
3
go.sum
@ -1284,6 +1284,8 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
|
|||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/Code-Hex/go-generics-cache v1.3.1 h1:i8rLwyhoyhaerr7JpjtYjJZUcCbWOdiYO3fZXLiEC4g=
|
github.com/Code-Hex/go-generics-cache v1.3.1 h1:i8rLwyhoyhaerr7JpjtYjJZUcCbWOdiYO3fZXLiEC4g=
|
||||||
github.com/Code-Hex/go-generics-cache v1.3.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4=
|
github.com/Code-Hex/go-generics-cache v1.3.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4=
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||||
github.com/FZambia/eagle v0.1.0 h1:9gyX6x+xjoIfglgyPTcYm7dvY7FJ93us1QY5De4CyXA=
|
github.com/FZambia/eagle v0.1.0 h1:9gyX6x+xjoIfglgyPTcYm7dvY7FJ93us1QY5De4CyXA=
|
||||||
github.com/FZambia/eagle v0.1.0/go.mod h1:YjGSPVkQTNcVLfzEUQJNgW9ScPR0K4u/Ky0yeFa4oDA=
|
github.com/FZambia/eagle v0.1.0/go.mod h1:YjGSPVkQTNcVLfzEUQJNgW9ScPR0K4u/Ky0yeFa4oDA=
|
||||||
@ -2459,6 +2461,7 @@ github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa
|
|||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||||
github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4=
|
github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4=
|
||||||
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
|
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
|
||||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
|
59
pkg/services/store/entity/db/dbimpl/db.go
Normal file
59
pkg/services/store/entity/db/dbimpl/db.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package dbimpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
entitydb "github.com/grafana/grafana/pkg/services/store/entity/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDB(d *sql.DB, driverName string) entitydb.DB {
|
||||||
|
return sqldb{
|
||||||
|
DB: d,
|
||||||
|
driverName: driverName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqldb struct {
|
||||||
|
*sql.DB
|
||||||
|
driverName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d sqldb) DriverName() string {
|
||||||
|
return d.driverName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d sqldb) BeginTx(ctx context.Context, opts *sql.TxOptions) (entitydb.Tx, error) {
|
||||||
|
t, err := d.DB.BeginTx(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tx{
|
||||||
|
Tx: t,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d sqldb) WithTx(ctx context.Context, opts *sql.TxOptions, f entitydb.TxFunc) error {
|
||||||
|
t, err := d.BeginTx(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("begin tx: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := f(ctx, t); err != nil {
|
||||||
|
if rollbackErr := t.Rollback(); rollbackErr != nil {
|
||||||
|
return fmt.Errorf("tx err: %w; rollback err: %w", err, rollbackErr)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("tx err: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = t.Commit(); err != nil {
|
||||||
|
return fmt.Errorf("commit err: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tx struct {
|
||||||
|
*sql.Tx
|
||||||
|
}
|
54
pkg/services/store/entity/db/dbimpl/db_test.go
Normal file
54
pkg/services/store/entity/db/dbimpl/db_test.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package dbimpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sqlmock "github.com/DATA-DOG/go-sqlmock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
entitydb "github.com/grafana/grafana/pkg/services/store/entity/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newCtx(t *testing.T) context.Context {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
d, ok := t.Deadline()
|
||||||
|
if !ok {
|
||||||
|
// provide a default timeout for tests
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
t.Cleanup(cancel)
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithDeadline(context.Background(), d)
|
||||||
|
t.Cleanup(cancel)
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDB_WithTx(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
newTxFunc := func(err error) entitydb.TxFunc {
|
||||||
|
return func(context.Context, entitydb.Tx) error {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("happy path", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
sqldb, mock, err := sqlmock.New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
db := NewDB(sqldb, "sqlmock")
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectCommit()
|
||||||
|
err = db.WithTx(newCtx(t), nil, newTxFunc(nil))
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
@ -4,6 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/dlmiddlecote/sqlstats"
|
"github.com/dlmiddlecote/sqlstats"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
@ -12,9 +16,6 @@ import (
|
|||||||
entitydb "github.com/grafana/grafana/pkg/services/store/entity/db"
|
entitydb "github.com/grafana/grafana/pkg/services/store/entity/db"
|
||||||
"github.com/grafana/grafana/pkg/services/store/entity/db/migrations"
|
"github.com/grafana/grafana/pkg/services/store/entity/db/migrations"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"xorm.io/xorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ entitydb.EntityDBInterface = (*EntityDB)(nil)
|
var _ entitydb.EntityDBInterface = (*EntityDB)(nil)
|
||||||
@ -128,3 +129,14 @@ func (db *EntityDB) GetSession() (*session.SessionDB, error) {
|
|||||||
func (db *EntityDB) GetCfg() *setting.Cfg {
|
func (db *EntityDB) GetCfg() *setting.Cfg {
|
||||||
return db.cfg
|
return db.cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *EntityDB) GetDB() (entitydb.DB, error) {
|
||||||
|
engine, err := db.GetEngine()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := NewDB(engine.DB().DB, engine.Dialect().DriverName())
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
@ -187,6 +187,20 @@ func initEntityTables(mg *migrator.Migrator) string {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
tables = append(tables, migrator.Table{
|
||||||
|
Name: "kind_version",
|
||||||
|
Columns: []*migrator.Column{
|
||||||
|
{Name: "group", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
|
||||||
|
{Name: "resource", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
|
||||||
|
{Name: "resource_version", Type: migrator.DB_BigInt, Nullable: false},
|
||||||
|
{Name: "created_at", Type: migrator.DB_BigInt, Nullable: false},
|
||||||
|
{Name: "updated_at", Type: migrator.DB_BigInt, Nullable: false},
|
||||||
|
},
|
||||||
|
Indices: []*migrator.Index{
|
||||||
|
{Cols: []string{"group", "resource"}, Type: migrator.UniqueIndex},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// Initialize all tables
|
// Initialize all tables
|
||||||
for t := range tables {
|
for t := range tables {
|
||||||
mg.AddMigration("drop table "+tables[t].Name, migrator.NewDropTableMigration(tables[t].Name))
|
mg.AddMigration("drop table "+tables[t].Name, migrator.NewDropTableMigration(tables[t].Name))
|
||||||
|
@ -1,16 +1,58 @@
|
|||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
// "github.com/grafana/grafana/pkg/services/sqlstore"
|
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/session"
|
"github.com/grafana/grafana/pkg/services/sqlstore/session"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DriverPostgres = "postgres"
|
||||||
|
DriverMySQL = "mysql"
|
||||||
|
DriverSQLite3 = "sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EntityDBInterface provides access to a database capable of supporting the
|
||||||
|
// Entity Server.
|
||||||
type EntityDBInterface interface {
|
type EntityDBInterface interface {
|
||||||
Init() error
|
Init() error
|
||||||
|
GetCfg() *setting.Cfg
|
||||||
|
GetDB() (DB, error)
|
||||||
|
|
||||||
|
// TODO: deprecate.
|
||||||
GetSession() (*session.SessionDB, error)
|
GetSession() (*session.SessionDB, error)
|
||||||
GetEngine() (*xorm.Engine, error)
|
GetEngine() (*xorm.Engine, error)
|
||||||
GetCfg() *setting.Cfg
|
}
|
||||||
|
|
||||||
|
// DB is a thin abstraction on *sql.DB to allow mocking to provide better unit
|
||||||
|
// testing. We purposefully hide database operation methods that would use
|
||||||
|
// context.Background().
|
||||||
|
type DB interface {
|
||||||
|
ContextExecer
|
||||||
|
BeginTx(context.Context, *sql.TxOptions) (Tx, error)
|
||||||
|
WithTx(context.Context, *sql.TxOptions, TxFunc) error
|
||||||
|
PingContext(context.Context) error
|
||||||
|
Stats() sql.DBStats
|
||||||
|
DriverName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type TxFunc = func(context.Context, Tx) error
|
||||||
|
|
||||||
|
type Tx interface {
|
||||||
|
ContextExecer
|
||||||
|
Exec(query string, args ...any) (sql.Result, error)
|
||||||
|
Query(query string, args ...any) (*sql.Rows, error)
|
||||||
|
QueryRow(query string, args ...any) *sql.Row
|
||||||
|
Commit() error
|
||||||
|
Rollback() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContextExecer interface {
|
||||||
|
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
|
||||||
|
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
|
||||||
|
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
DELETE FROM {{ .Ident "entity" }}
|
||||||
|
WHERE 1 = 1
|
||||||
|
AND {{ .Ident "namespace" }} = {{ .Arg .Key.Namespace }}
|
||||||
|
AND {{ .Ident "group" }} = {{ .Arg .Key.Group }}
|
||||||
|
AND {{ .Ident "resource" }} = {{ .Arg .Key.Resource }}
|
||||||
|
AND {{ .Ident "name" }} = {{ .Arg .Key.Name }}
|
||||||
|
;
|
@ -0,0 +1,35 @@
|
|||||||
|
INSERT INTO {{ .Ident "entity_folder" }}
|
||||||
|
(
|
||||||
|
{{ .Ident "guid" }},
|
||||||
|
{{ .Ident "namespace" }},
|
||||||
|
{{ .Ident "name" }},
|
||||||
|
{{ .Ident "slug_path" }},
|
||||||
|
{{ .Ident "tree" }},
|
||||||
|
{{ .Ident "depth" }},
|
||||||
|
{{ .Ident "lft" }},
|
||||||
|
{{ .Ident "rgt" }},
|
||||||
|
{{ .Ident "detached" }}
|
||||||
|
)
|
||||||
|
|
||||||
|
VALUES
|
||||||
|
{{ $this := . }}
|
||||||
|
{{ $addComma := false }}
|
||||||
|
{{ range .Items }}
|
||||||
|
{{ if $addComma }}
|
||||||
|
,
|
||||||
|
{{ end }}
|
||||||
|
{{ $addComma = true }}
|
||||||
|
|
||||||
|
(
|
||||||
|
{{ $this.Arg .GUID }},
|
||||||
|
{{ $this.Arg .Namespace }},
|
||||||
|
{{ $this.Arg .UID }},
|
||||||
|
{{ $this.Arg .SlugPath }},
|
||||||
|
{{ $this.Arg .JS }},
|
||||||
|
{{ $this.Arg .Depth }},
|
||||||
|
{{ $this.Arg .Left }},
|
||||||
|
{{ $this.Arg .Right }},
|
||||||
|
{{ $this.Arg .Detached }}
|
||||||
|
)
|
||||||
|
{{ end }}
|
||||||
|
;
|
93
pkg/services/store/entity/sqlstash/data/entity_insert.sql
Normal file
93
pkg/services/store/entity/sqlstash/data/entity_insert.sql
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
INSERT INTO
|
||||||
|
|
||||||
|
{{/* Determine which table to insert into */}}
|
||||||
|
{{ if .TableEntity }} {{ .Ident "entity" }}
|
||||||
|
{{ else }} {{ .Ident "entity_history" }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Explicitly specify fields that will be set */}}
|
||||||
|
(
|
||||||
|
{{ .Ident "guid" }},
|
||||||
|
{{ .Ident "resource_version" }},
|
||||||
|
|
||||||
|
{{ .Ident "key" }},
|
||||||
|
|
||||||
|
{{ .Ident "group" }},
|
||||||
|
{{ .Ident "group_version" }},
|
||||||
|
{{ .Ident "resource" }},
|
||||||
|
{{ .Ident "namespace" }},
|
||||||
|
{{ .Ident "name" }},
|
||||||
|
|
||||||
|
{{ .Ident "folder" }},
|
||||||
|
|
||||||
|
{{ .Ident "meta" }},
|
||||||
|
{{ .Ident "body" }},
|
||||||
|
{{ .Ident "status" }},
|
||||||
|
|
||||||
|
{{ .Ident "size" }},
|
||||||
|
{{ .Ident "etag" }},
|
||||||
|
|
||||||
|
{{ .Ident "created_at" }},
|
||||||
|
{{ .Ident "created_by" }},
|
||||||
|
{{ .Ident "updated_at" }},
|
||||||
|
{{ .Ident "updated_by" }},
|
||||||
|
|
||||||
|
{{ .Ident "origin" }},
|
||||||
|
{{ .Ident "origin_key" }},
|
||||||
|
{{ .Ident "origin_ts" }},
|
||||||
|
|
||||||
|
{{ .Ident "title" }},
|
||||||
|
{{ .Ident "slug" }},
|
||||||
|
{{ .Ident "description" }},
|
||||||
|
|
||||||
|
{{ .Ident "message" }},
|
||||||
|
{{ .Ident "labels" }},
|
||||||
|
{{ .Ident "fields" }},
|
||||||
|
{{ .Ident "errors" }},
|
||||||
|
|
||||||
|
{{ .Ident "action" }}
|
||||||
|
)
|
||||||
|
|
||||||
|
{{/* Provide the values */}}
|
||||||
|
VALUES (
|
||||||
|
{{ .Arg .Entity.Guid }},
|
||||||
|
{{ .Arg .Entity.ResourceVersion }},
|
||||||
|
|
||||||
|
{{ .Arg .Entity.Key }},
|
||||||
|
|
||||||
|
{{ .Arg .Entity.Group }},
|
||||||
|
{{ .Arg .Entity.GroupVersion }},
|
||||||
|
{{ .Arg .Entity.Resource }},
|
||||||
|
{{ .Arg .Entity.Namespace }},
|
||||||
|
{{ .Arg .Entity.Name }},
|
||||||
|
|
||||||
|
{{ .Arg .Entity.Folder }},
|
||||||
|
|
||||||
|
{{ .Arg .Entity.Meta }},
|
||||||
|
{{ .Arg .Entity.Body }},
|
||||||
|
{{ .Arg .Entity.Status }},
|
||||||
|
|
||||||
|
{{ .Arg .Entity.Size }},
|
||||||
|
{{ .Arg .Entity.ETag }},
|
||||||
|
|
||||||
|
{{ .Arg .Entity.CreatedAt }},
|
||||||
|
{{ .Arg .Entity.CreatedBy }},
|
||||||
|
{{ .Arg .Entity.UpdatedAt }},
|
||||||
|
{{ .Arg .Entity.UpdatedBy }},
|
||||||
|
|
||||||
|
{{ .Arg .Entity.Origin.Source }},
|
||||||
|
{{ .Arg .Entity.Origin.Key }},
|
||||||
|
{{ .Arg .Entity.Origin.Time }},
|
||||||
|
|
||||||
|
{{ .Arg .Entity.Title }},
|
||||||
|
{{ .Arg .Entity.Slug }},
|
||||||
|
{{ .Arg .Entity.Description }},
|
||||||
|
|
||||||
|
{{ .Arg .Entity.Message }},
|
||||||
|
{{ .Arg .Entity.Labels }},
|
||||||
|
{{ .Arg .Entity.Fields }},
|
||||||
|
{{ .Arg .Entity.Errors }},
|
||||||
|
|
||||||
|
{{ .Arg .Entity.Action }}
|
||||||
|
)
|
||||||
|
;
|
@ -0,0 +1,18 @@
|
|||||||
|
DELETE FROM {{ .Ident "entity_labels" }}
|
||||||
|
WHERE 1 = 1
|
||||||
|
AND {{ .Ident "guid" }} = {{ .Arg .GUID }}
|
||||||
|
{{ if gt (len .KeepLabels) 0 }}
|
||||||
|
AND {{ .Ident "label" }} NOT IN (
|
||||||
|
{{ $this := . }}
|
||||||
|
{{ $addComma := false }}
|
||||||
|
{{ range .KeepLabels }}
|
||||||
|
{{ if $addComma }}
|
||||||
|
,
|
||||||
|
{{ end }}
|
||||||
|
{{ $addComma = true }}
|
||||||
|
|
||||||
|
{{ $this.Arg . }}
|
||||||
|
{{ end }}
|
||||||
|
)
|
||||||
|
{{ end }}
|
||||||
|
;
|
@ -0,0 +1,29 @@
|
|||||||
|
INSERT INTO {{ .Ident "entity_labels" }}
|
||||||
|
(
|
||||||
|
{{ .Ident "guid" }},
|
||||||
|
{{ .Ident "label" }},
|
||||||
|
{{ .Ident "value" }}
|
||||||
|
)
|
||||||
|
|
||||||
|
VALUES
|
||||||
|
{{/*
|
||||||
|
When we enter the "range" loop the "." will be changed, so we need to
|
||||||
|
store the current ".GUID" in a variable to be able to use its value
|
||||||
|
*/}}
|
||||||
|
{{ $guid := .GUID }}
|
||||||
|
|
||||||
|
{{ $this := . }}
|
||||||
|
{{ $addComma := false }}
|
||||||
|
{{ range $name, $value := .Labels }}
|
||||||
|
{{ if $addComma }}
|
||||||
|
,
|
||||||
|
{{ end }}
|
||||||
|
{{ $addComma = true }}
|
||||||
|
|
||||||
|
(
|
||||||
|
{{ $this.Arg $guid }},
|
||||||
|
{{ $this.Arg $name }},
|
||||||
|
{{ $this.Arg $value }}
|
||||||
|
)
|
||||||
|
{{ end }}
|
||||||
|
;
|
@ -0,0 +1,14 @@
|
|||||||
|
SELECT
|
||||||
|
{{ .Ident "guid" | .Into .FolderInfo.GUID }},
|
||||||
|
{{ .Ident "name" | .Into .FolderInfo.UID }},
|
||||||
|
{{ .Ident "folder" | .Into .FolderInfo.ParentUID }},
|
||||||
|
{{ .Ident "name" | .Into .FolderInfo.Name }},
|
||||||
|
{{ .Ident "slug" | .Into .FolderInfo.Slug }}
|
||||||
|
|
||||||
|
FROM {{ .Ident "entity" }}
|
||||||
|
|
||||||
|
WHERE 1 = 1
|
||||||
|
AND {{ .Ident "group" }} = {{ .Arg .Group }}
|
||||||
|
AND {{ .Ident "resource" }} = {{ .Arg .Resource }}
|
||||||
|
AND {{ .Ident "namespace" }} = {{ .Arg .Namespace }}
|
||||||
|
;
|
78
pkg/services/store/entity/sqlstash/data/entity_read.sql
Normal file
78
pkg/services/store/entity/sqlstash/data/entity_read.sql
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
SELECT
|
||||||
|
{{ .Ident "guid" | .Into .Entity.Guid }},
|
||||||
|
{{ .Ident "resource_version" | .Into .Entity.ResourceVersion }},
|
||||||
|
|
||||||
|
{{ .Ident "key" | .Into .Entity.Key }},
|
||||||
|
|
||||||
|
{{ .Ident "group" | .Into .Entity.Group }},
|
||||||
|
{{ .Ident "group_version" | .Into .Entity.GroupVersion }},
|
||||||
|
{{ .Ident "resource" | .Into .Entity.Resource }},
|
||||||
|
{{ .Ident "namespace" | .Into .Entity.Namespace }},
|
||||||
|
{{ .Ident "name" | .Into .Entity.Name }},
|
||||||
|
|
||||||
|
{{ .Ident "folder" | .Into .Entity.Folder }},
|
||||||
|
|
||||||
|
{{ .Ident "meta" | .Into .Entity.Meta }},
|
||||||
|
{{ .Ident "body" | .Into .Entity.Body }},
|
||||||
|
{{ .Ident "status" | .Into .Entity.Status }},
|
||||||
|
|
||||||
|
{{ .Ident "size" | .Into .Entity.Size }},
|
||||||
|
{{ .Ident "etag" | .Into .Entity.ETag }},
|
||||||
|
|
||||||
|
{{ .Ident "created_at" | .Into .Entity.CreatedAt }},
|
||||||
|
{{ .Ident "created_by" | .Into .Entity.CreatedBy }},
|
||||||
|
{{ .Ident "updated_at" | .Into .Entity.UpdatedAt }},
|
||||||
|
{{ .Ident "updated_by" | .Into .Entity.UpdatedBy }},
|
||||||
|
|
||||||
|
{{ .Ident "origin" | .Into .Entity.Origin.Source }},
|
||||||
|
{{ .Ident "origin_key" | .Into .Entity.Origin.Key }},
|
||||||
|
{{ .Ident "origin_ts" | .Into .Entity.Origin.Time }},
|
||||||
|
|
||||||
|
{{ .Ident "title" | .Into .Entity.Title }},
|
||||||
|
{{ .Ident "slug" | .Into .Entity.Slug }},
|
||||||
|
{{ .Ident "description" | .Into .Entity.Description }},
|
||||||
|
|
||||||
|
{{ .Ident "message" | .Into .Entity.Message }},
|
||||||
|
{{ .Ident "labels" | .Into .Entity.Labels }},
|
||||||
|
{{ .Ident "fields" | .Into .Entity.Fields }},
|
||||||
|
{{ .Ident "errors" | .Into .Entity.Errors }},
|
||||||
|
|
||||||
|
{{ .Ident "action" | .Into .Entity.Action }}
|
||||||
|
|
||||||
|
FROM
|
||||||
|
{{ if gt .ResourceVersion 0 }}
|
||||||
|
{{ .Ident "entity_history" }}
|
||||||
|
{{ else }}
|
||||||
|
{{ .Ident "entity" }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
WHERE 1 = 1
|
||||||
|
AND {{ .Ident "namespace" }} = {{ .Arg .Key.Namespace }}
|
||||||
|
AND {{ .Ident "group" }} = {{ .Arg .Key.Group }}
|
||||||
|
AND {{ .Ident "resource" }} = {{ .Arg .Key.Resource }}
|
||||||
|
AND {{ .Ident "name" }} = {{ .Arg .Key.Name }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Resource versions work like snapshots at the kind level. Thus, a request
|
||||||
|
to retrieve a specific resource version should be interpreted as asking
|
||||||
|
for a resource as of how it existed at that point in time. This is why we
|
||||||
|
request matching entities with at most the provided resource version, and
|
||||||
|
return only the one with the highest resource version. In the case of not
|
||||||
|
specifying a resource version (i.e. resource version zero), it is
|
||||||
|
interpreted as the latest version of the given entity, thus we instead
|
||||||
|
query the "entity" table (which holds only the latest version of
|
||||||
|
non-deleted entities) and we don't need to specify anything else. The
|
||||||
|
"entity" table has a unique constraint on (namespace, group, resource,
|
||||||
|
name), so we're guaranteed to have at most one matching row.
|
||||||
|
*/}}
|
||||||
|
{{ if gt .ResourceVersion 0 }}
|
||||||
|
AND {{ .Ident "resource_version" }} <= {{ .Arg .ResourceVersion }}
|
||||||
|
|
||||||
|
ORDER BY {{ .Ident "resource_version" }} DESC
|
||||||
|
LIMIT 1
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if .SelectForUpdate }}
|
||||||
|
{{ .SelectFor "UPDATE" }}
|
||||||
|
{{ end }}
|
||||||
|
;
|
53
pkg/services/store/entity/sqlstash/data/entity_ref_find.sql
Normal file
53
pkg/services/store/entity/sqlstash/data/entity_ref_find.sql
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
SELECT
|
||||||
|
e.{{ .Ident "guid" | .Into .Entity.Guid }},
|
||||||
|
e.{{ .Ident "resource_version" | .Into .Entity.ResourceVersion }},
|
||||||
|
|
||||||
|
e.{{ .Ident "key" | .Into .Entity.Key }},
|
||||||
|
|
||||||
|
e.{{ .Ident "group" | .Into .Entity.Group }},
|
||||||
|
e.{{ .Ident "group_version" | .Into .Entity.GroupVersion }},
|
||||||
|
e.{{ .Ident "resource" | .Into .Entity.Resource }},
|
||||||
|
e.{{ .Ident "namespace" | .Into .Entity.Namespace }},
|
||||||
|
e.{{ .Ident "name" | .Into .Entity.Name }},
|
||||||
|
|
||||||
|
e.{{ .Ident "folder" | .Into .Entity.Folder }},
|
||||||
|
|
||||||
|
e.{{ .Ident "meta" | .Into .Entity.Meta }},
|
||||||
|
e.{{ .Ident "body" | .Into .Entity.Body }},
|
||||||
|
e.{{ .Ident "status" | .Into .Entity.Status }},
|
||||||
|
|
||||||
|
e.{{ .Ident "size" | .Into .Entity.Size }},
|
||||||
|
e.{{ .Ident "etag" | .Into .Entity.ETag }},
|
||||||
|
|
||||||
|
e.{{ .Ident "created_at" | .Into .Entity.CreatedAt }},
|
||||||
|
e.{{ .Ident "created_by" | .Into .Entity.CreatedBy }},
|
||||||
|
e.{{ .Ident "updated_at" | .Into .Entity.UpdatedAt }},
|
||||||
|
e.{{ .Ident "updated_by" | .Into .Entity.UpdatedBy }},
|
||||||
|
|
||||||
|
e.{{ .Ident "origin" | .Into .Entity.Origin.Source }},
|
||||||
|
e.{{ .Ident "origin_key" | .Into .Entity.Origin.Key }},
|
||||||
|
e.{{ .Ident "origin_ts" | .Into .Entity.Origin.Time }},
|
||||||
|
|
||||||
|
e.{{ .Ident "title" | .Into .Entity.Title }},
|
||||||
|
e.{{ .Ident "slug" | .Into .Entity.Slug }},
|
||||||
|
e.{{ .Ident "description" | .Into .Entity.Description }},
|
||||||
|
|
||||||
|
e.{{ .Ident "message" | .Into .Entity.Message }},
|
||||||
|
e.{{ .Ident "labels" | .Into .Entity.Labels }},
|
||||||
|
e.{{ .Ident "fields" | .Into .Entity.Fields }},
|
||||||
|
e.{{ .Ident "errors" | .Into .Entity.Errors }},
|
||||||
|
|
||||||
|
e.{{ .Ident "action" | .Into .Entity.Action }}
|
||||||
|
|
||||||
|
FROM
|
||||||
|
{{ .Ident "entity_ref" }} AS r
|
||||||
|
INNER JOIN
|
||||||
|
{{ .Ident "entity" }} AS e
|
||||||
|
ON r.{{ .Ident "guid" }} = e.{{ .Ident "guid" }}
|
||||||
|
|
||||||
|
WHERE 1 = 1
|
||||||
|
AND r.{{ .Ident "namespace" }} = {{ .Arg .Request.Namespace }}
|
||||||
|
AND r.{{ .Ident "group" }} = {{ .Arg .Request.Group }}
|
||||||
|
AND r.{{ .Ident "resource" }} = {{ .Arg .Request.Resource }}
|
||||||
|
AND r.{{ .Ident "resolved_to" }} = {{ .Arg .Request.Name }}
|
||||||
|
;
|
34
pkg/services/store/entity/sqlstash/data/entity_update.sql
Normal file
34
pkg/services/store/entity/sqlstash/data/entity_update.sql
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
UPDATE {{ .Ident "entity" }} SET
|
||||||
|
{{ .Ident "resource_version" }} = {{ .Arg .Entity.ResourceVersion }},
|
||||||
|
|
||||||
|
{{ .Ident "group_version" }} = {{ .Arg .Entity.GroupVersion }},
|
||||||
|
|
||||||
|
{{ .Ident "folder" }} = {{ .Arg .Entity.Folder }},
|
||||||
|
|
||||||
|
{{ .Ident "meta" }} = {{ .Arg .Entity.Meta }},
|
||||||
|
{{ .Ident "body" }} = {{ .Arg .Entity.Body }},
|
||||||
|
{{ .Ident "status" }} = {{ .Arg .Entity.Status }},
|
||||||
|
|
||||||
|
{{ .Ident "size" }} = {{ .Arg .Entity.Size }},
|
||||||
|
{{ .Ident "etag" }} = {{ .Arg .Entity.ETag }},
|
||||||
|
|
||||||
|
{{ .Ident "updated_at" }} = {{ .Arg .Entity.UpdatedAt }},
|
||||||
|
{{ .Ident "updated_by" }} = {{ .Arg .Entity.UpdatedBy }},
|
||||||
|
|
||||||
|
{{ .Ident "origin" }} = {{ .Arg .Entity.Origin.Source }},
|
||||||
|
{{ .Ident "origin_key" }} = {{ .Arg .Entity.Origin.Key }},
|
||||||
|
{{ .Ident "origin_ts" }} = {{ .Arg .Entity.Origin.Time }},
|
||||||
|
|
||||||
|
{{ .Ident "title" }} = {{ .Arg .Entity.Title }},
|
||||||
|
{{ .Ident "slug" }} = {{ .Arg .Entity.Slug }},
|
||||||
|
{{ .Ident "description" }} = {{ .Arg .Entity.Description }},
|
||||||
|
|
||||||
|
{{ .Ident "message" }} = {{ .Arg .Entity.Message }},
|
||||||
|
{{ .Ident "labels" }} = {{ .Arg .Entity.Labels }},
|
||||||
|
{{ .Ident "fields" }} = {{ .Arg .Entity.Fields }},
|
||||||
|
{{ .Ident "errors" }} = {{ .Arg .Entity.Errors }},
|
||||||
|
|
||||||
|
{{ .Ident "action" }} = {{ .Arg .Entity.Action }}
|
||||||
|
|
||||||
|
WHERE {{ .Ident "guid" }} = {{ .Arg .Entity.Guid }}
|
||||||
|
;
|
@ -0,0 +1,7 @@
|
|||||||
|
UPDATE {{ .Ident "kind_version" }}
|
||||||
|
SET {{ .Ident "resource_version" }} = {{ .Arg .ResourceVersion }} + 1
|
||||||
|
WHERE 1 = 1
|
||||||
|
AND {{ .Ident "group" }} = {{ .Arg .Group }}
|
||||||
|
AND {{ .Ident "resource" }} = {{ .Arg .Resource }}
|
||||||
|
AND {{ .Ident "resource_version" }} = {{ .Arg .ResourceVersion }}
|
||||||
|
;
|
@ -0,0 +1,13 @@
|
|||||||
|
INSERT INTO {{ .Ident "kind_version" }}
|
||||||
|
(
|
||||||
|
{{ .Ident "group" }},
|
||||||
|
{{ .Ident "resource" }},
|
||||||
|
{{ .Ident "resource_version" }}
|
||||||
|
)
|
||||||
|
|
||||||
|
VALUES (
|
||||||
|
{{ .Arg .Group }},
|
||||||
|
{{ .Arg .Resource }},
|
||||||
|
1
|
||||||
|
)
|
||||||
|
;
|
@ -0,0 +1,7 @@
|
|||||||
|
SELECT {{ .Ident "resource_version" | .Into .ResourceVersion }}
|
||||||
|
FROM {{ .Ident "kind_version" }}
|
||||||
|
WHERE 1 = 1
|
||||||
|
AND {{ .Ident "group" }} = {{ .Arg .Group }}
|
||||||
|
AND {{ .Ident "resource" }} = {{ .Arg .Resource }}
|
||||||
|
{{ .SelectFor "UPDATE" }}
|
||||||
|
;
|
164
pkg/services/store/entity/sqlstash/queries.go
Normal file
164
pkg/services/store/entity/sqlstash/queries.go
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
package sqlstash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||||
|
"github.com/grafana/grafana/pkg/services/store/entity/sqlstash/sqltemplate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Templates.
|
||||||
|
var (
|
||||||
|
//go:embed data
|
||||||
|
templatesFs embed.FS
|
||||||
|
|
||||||
|
// all templates
|
||||||
|
templates = template.Must(template.ParseFS(templatesFs, `data/*.sql`))
|
||||||
|
|
||||||
|
sqlEntityDelete = getTemplate("entity_delete.sql")
|
||||||
|
sqlEntityInsert = getTemplate("entity_insert.sql")
|
||||||
|
sqlEntityListFolderElements = getTemplate("entity_list_folder_elements.sql")
|
||||||
|
sqlEntityUpdate = getTemplate("entity_update.sql")
|
||||||
|
sqlEntityRead = getTemplate("entity_read.sql")
|
||||||
|
|
||||||
|
sqlEntityFolderInsert = getTemplate("entity_folder_insert.sql")
|
||||||
|
|
||||||
|
sqlEntityRefFind = getTemplate("entity_ref_find.sql")
|
||||||
|
|
||||||
|
sqlEntityLabelsDelete = getTemplate("entity_labels_delete.sql")
|
||||||
|
sqlEntityLabelsInsert = getTemplate("entity_labels_insert.sql")
|
||||||
|
|
||||||
|
sqlKindVersionInc = getTemplate("kind_version_inc.sql")
|
||||||
|
sqlKindVersionInsert = getTemplate("kind_version_insert.sql")
|
||||||
|
sqlKindVersionLock = getTemplate("kind_version_lock.sql")
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTemplate(filename string) *template.Template {
|
||||||
|
if t := templates.Lookup(filename); t != nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("template file not found: %s", filename))
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlEntityFolderInsertRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
Items []*sqlEntityFolderInsertRequestItem
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlEntityFolderInsertRequestItem struct {
|
||||||
|
GUID string
|
||||||
|
Namespace string
|
||||||
|
UID string
|
||||||
|
SlugPath string
|
||||||
|
JS string
|
||||||
|
Depth int32
|
||||||
|
Left int32
|
||||||
|
Right int32
|
||||||
|
Detached bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlEntityRefFindRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
Request *entity.ReferenceRequest
|
||||||
|
Entity *withSerialized
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlEntityLabelsInsertRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
GUID string
|
||||||
|
Labels map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlEntityLabelsDeleteRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
GUID string
|
||||||
|
KeepLabels []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlKindVersionLockRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
Group string
|
||||||
|
GroupVersion string
|
||||||
|
Resource string
|
||||||
|
ResourceVersion int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlKindVersionIncRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
Group string
|
||||||
|
GroupVersion string
|
||||||
|
Resource string
|
||||||
|
ResourceVersion int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlKindVersionInsertRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
Group string
|
||||||
|
GroupVersion string
|
||||||
|
Resource string
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlEntityListFolderElementsRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
Group string
|
||||||
|
Resource string
|
||||||
|
Namespace string
|
||||||
|
FolderInfo *folderInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlEntityReadRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
Key *entity.Key
|
||||||
|
ResourceVersion int64
|
||||||
|
SelectForUpdate bool
|
||||||
|
Entity *withSerialized
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlEntityDeleteRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
Key *entity.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlEntityInsertRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
Entity *withSerialized
|
||||||
|
|
||||||
|
// TableEntity, when true, means we will insert into table "entity", and
|
||||||
|
// into table "entity_history" otherwise.
|
||||||
|
TableEntity bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlEntityUpdateRequest struct {
|
||||||
|
*sqltemplate.SQLTemplate
|
||||||
|
Entity *withSerialized
|
||||||
|
}
|
||||||
|
|
||||||
|
// withSerialized provides access to the wire Entiity DTO as well as the
|
||||||
|
// serialized version of some of its fields suitable to be read from or written
|
||||||
|
// to the database.
|
||||||
|
type withSerialized struct {
|
||||||
|
*entity.Entity
|
||||||
|
|
||||||
|
Labels []byte
|
||||||
|
Fields []byte
|
||||||
|
Errors []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove once we start using these symbols. Prevents `unused` linter
|
||||||
|
// until the next PR.
|
||||||
|
var (
|
||||||
|
_, _, _ = sqlEntityDelete, sqlEntityInsert, sqlEntityListFolderElements
|
||||||
|
_, _, _ = sqlEntityUpdate, sqlEntityRead, sqlEntityFolderInsert
|
||||||
|
_, _, _ = sqlEntityRefFind, sqlEntityLabelsDelete, sqlEntityLabelsInsert
|
||||||
|
_, _, _ = sqlKindVersionInc, sqlKindVersionInsert, sqlKindVersionLock
|
||||||
|
_, _ = sqlEntityFolderInsertRequest{}, sqlEntityFolderInsertRequestItem{}
|
||||||
|
_, _ = sqlEntityRefFindRequest{}, sqlEntityLabelsInsertRequest{}
|
||||||
|
_, _ = sqlEntityLabelsInsertRequest{}, sqlEntityLabelsDeleteRequest{}
|
||||||
|
_, _ = sqlKindVersionLockRequest{}, sqlKindVersionIncRequest{}
|
||||||
|
_, _ = sqlKindVersionInsertRequest{}, sqlEntityListFolderElementsRequest{}
|
||||||
|
_, _ = sqlEntityReadRequest{}, sqlEntityDeleteRequest{}
|
||||||
|
_, _ = sqlEntityInsertRequest{}, sqlEntityUpdateRequest{}
|
||||||
|
_ = withSerialized{}
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user