provide dynamic databases

This commit is contained in:
Andy Arthur
2021-07-12 16:00:07 -07:00
parent 58e39f42ec
commit dc81473219
6 changed files with 110 additions and 46 deletions
+5 -4
View File
@@ -109,7 +109,8 @@ func Serve(ctx context.Context, version string, serverConfig ServerConfig, serve
}
dbs := commands.CollectDBs(mrEnv)
cat := sql.NewCatalogWithDbProvider(dsqle.NewDoltDatabaseProvider(dbs...))
pro := dsqle.NewDoltDatabaseProvider(dbs...)
cat := sql.NewCatalogWithDbProvider(pro)
cat.AddDatabase(information_schema.NewInformationSchemaDatabase(cat))
a := analyzer.NewBuilder(cat).WithParallelism(serverConfig.QueryParallelism()).Build()
@@ -141,7 +142,7 @@ func Serve(ctx context.Context, version string, serverConfig ServerConfig, serve
// to the value of mysql that we support.
},
sqlEngine,
newSessionBuilder(sqlEngine, username, email, mrEnv, serverConfig.AutoCommit()),
newSessionBuilder(sqlEngine, username, email, pro, mrEnv, serverConfig.AutoCommit()),
)
if startError != nil {
@@ -168,7 +169,7 @@ func portInUse(hostPort string) bool {
return false
}
func newSessionBuilder(sqlEngine *sqle.Engine, username, email string, mrEnv env.MultiRepoEnv, autocommit bool) server.SessionBuilder {
func newSessionBuilder(sqlEngine *sqle.Engine, username string, email string, pro dsqle.DoltDatabaseProvider, mrEnv env.MultiRepoEnv, autocommit bool) server.SessionBuilder {
return func(ctx context.Context, conn *mysql.Conn, host string) (sql.Session, *sql.IndexRegistry, *sql.ViewRegistry, error) {
tmpSqlCtx := sql.NewEmptyContext()
@@ -180,7 +181,7 @@ func newSessionBuilder(sqlEngine *sqle.Engine, username, email string, mrEnv env
return nil, nil, nil, err
}
doltSess, err := dsess.NewSession(tmpSqlCtx, mysqlSess, username, email, dbStates...)
doltSess, err := dsess.NewSession(tmpSqlCtx, mysqlSess, pro, username, email, dbStates...)
if err != nil {
return nil, nil, nil, err
}
+15 -13
View File
@@ -44,17 +44,13 @@ func init() {
}
}
type RevisionDatabaseProvider interface {
RevisionDbState(ctx context.Context, revDB string) (dsess.InitialDbState, error)
}
type DoltDatabaseProvider struct {
databases map[string]sql.Database
}
var _ sql.DatabaseProvider = DoltDatabaseProvider{}
var _ sql.MutableDatabaseProvider = DoltDatabaseProvider{}
var _ RevisionDatabaseProvider = DoltDatabaseProvider{}
var _ dsess.RevisionDatabaseProvider = DoltDatabaseProvider{}
func NewDoltDatabaseProvider(databases ...Database) DoltDatabaseProvider {
dbs := make(map[string]sql.Database, len(databases))
@@ -174,15 +170,19 @@ func (p DoltDatabaseProvider) RevisionDbState(ctx context.Context, revDB string)
return init, nil
}
if doltdb.IsValidCommitHash(revSpec) {
_, init, err := dbRevisionForCommit(ctx, srcDb, revSpec)
if err != nil {
return dsess.InitialDbState{}, err
}
return init, nil
}
// TODO: allow database revisions at commits
// much of the current database state logic depends on having
// a WorkingSet to reference, but Commits in the history don't
// have corresponding WorkingSets.
//if doltdb.IsValidCommitHash(revSpec) {
// _, init, err := dbRevisionForCommit(ctx, srcDb, revSpec)
// if err != nil {
// return dsess.InitialDbState{}, err
// }
// return init, nil
//}
return dsess.InitialDbState{}, err
return dsess.InitialDbState{}, sql.ErrDatabaseNotFound.New(revDB)
}
func isBranch(ctx context.Context, ddb *doltdb.DoltDB, revSpec string) bool {
@@ -269,6 +269,8 @@ func dbRevisionForCommit(ctx context.Context, srcDb Database, revSpec string) (R
init := dsess.InitialDbState{
Db: db,
HeadCommit: cm,
// TODO: provide a working root without a working set
//WorkingSet: nil
DbData: env.DbData{
Ddb: srcDb.ddb,
Rsw: srcDb.rsw,
@@ -0,0 +1,37 @@
// Copyright 2021 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dsess
import (
"context"
"github.com/dolthub/go-mysql-server/sql"
)
// RevisionDatabaseProvider provides revision databases.
// In Dolt, commits and branches can be accessed as discreet databases
// using a Dolt-specific syntax: `my_database/my_branch`. Revision databases
// corresponding to historical commits in the repository will be read-only
// databases. Revision databases for branches will be read/write.
type RevisionDatabaseProvider interface {
// RevisionDbState provides the InitialDbState for a revision database.
RevisionDbState(ctx context.Context, revDB string) (InitialDbState, error)
}
type emptyRevisionDatabaseProvider struct{}
func (e emptyRevisionDatabaseProvider) RevisionDbState(_ context.Context, revDB string) (InitialDbState, error) {
return InitialDbState{}, sql.ErrDatabaseNotFound.New(revDB)
}
+24 -26
View File
@@ -89,22 +89,6 @@ func init() {
})
}
func TransactionsDisabled(ctx *sql.Context) bool {
enabled, err := ctx.GetSessionVariable(ctx, TransactionsDisabledSysVar)
if err != nil {
panic(err)
}
switch enabled.(int8) {
case 0:
return false
case 1:
return true
default:
panic(fmt.Sprintf("Unexpected value %v", enabled))
}
}
func IsHeadKey(key string) (bool, string) {
if strings.HasSuffix(key, HeadKeySuffix) {
return true, key[:len(key)-len(HeadKeySuffix)]
@@ -127,8 +111,8 @@ type Session struct {
BatchMode batchMode
Username string
Email string
// TODO: make this private again
dbStates map[string]*DatabaseSessionState
dbStates map[string]*DatabaseSessionState
provider RevisionDatabaseProvider
}
type DatabaseSessionState struct {
@@ -162,6 +146,7 @@ func DefaultSession() *Session {
Username: "",
Email: "",
dbStates: make(map[string]*DatabaseSessionState),
provider: emptyRevisionDatabaseProvider{},
}
return sess
}
@@ -175,12 +160,13 @@ type InitialDbState struct {
}
// NewSession creates a Session object from a standard sql.Session and 0 or more Database objects.
func NewSession(ctx *sql.Context, sqlSess sql.Session, username, email string, dbs ...InitialDbState) (*Session, error) {
func NewSession(ctx *sql.Context, sqlSess sql.Session, pro RevisionDatabaseProvider, username, email string, dbs ...InitialDbState) (*Session, error) {
sess := &Session{
Session: sqlSess,
Username: username,
Email: email,
dbStates: make(map[string]*DatabaseSessionState),
provider: pro,
}
for _, db := range dbs {
@@ -208,6 +194,25 @@ func DSessFromSess(sess sql.Session) *Session {
func (sess *Session) LookupDbState(ctx *sql.Context, dbName string) (*DatabaseSessionState, bool, error) {
dbState, ok := sess.dbStates[dbName]
if ok {
return dbState, ok, nil
}
init, err := sess.provider.RevisionDbState(ctx, dbName)
if err != nil {
return nil, ok, err
}
// TODO: this could potentially add a |sess.dbStates| entry
// for every commit in the history, leaking memory.
// We need a size-limited data structure for read-only
// revision databases reading from Commits.
if err = sess.AddDB(ctx, init); err != nil {
return nil, ok, err
}
dbState, ok = sess.dbStates[dbName]
if !ok {
return nil, ok, sql.ErrDatabaseNotFound.New(dbName)
}
return dbState, ok, nil
}
@@ -228,13 +233,6 @@ func (sess *Session) Flush(ctx *sql.Context, dbName string) error {
return sess.SetRoot(ctx, dbName, newRoot)
}
// DisabledTransaction is a no-op transaction type that lets us feature-gate transaction logic changes
type DisabledTransaction struct{}
func (d DisabledTransaction) String() string {
return "Disabled transaction"
}
// CommitTransaction commits the in-progress transaction for the database named
func (sess *Session) StartTransaction(ctx *sql.Context, dbName string) (sql.Transaction, error) {
if TransactionsDisabled(ctx) {
@@ -32,6 +32,29 @@ const (
maxTxCommitRetries = 5
)
func TransactionsDisabled(ctx *sql.Context) bool {
enabled, err := ctx.GetSessionVariable(ctx, TransactionsDisabledSysVar)
if err != nil {
panic(err)
}
switch enabled.(int8) {
case 0:
return false
case 1:
return true
default:
panic(fmt.Sprintf("Unexpected value %v", enabled))
}
}
// DisabledTransaction is a no-op transaction type that lets us feature-gate transaction logic changes
type DisabledTransaction struct{}
func (d DisabledTransaction) String() string {
return "Disabled transaction"
}
type DoltTransaction struct {
startState *doltdb.WorkingSet
workingSetRef ref.WorkingSetRef
@@ -51,7 +51,8 @@ var _ enginetest.KeylessTableHarness = (*DoltHarness)(nil)
var _ enginetest.ReadOnlyDatabaseHarness = (*DoltHarness)(nil)
func newDoltHarness(t *testing.T) *DoltHarness {
session, err := dsess.NewSession(sql.NewEmptyContext(), enginetest.NewBaseSession(), "test", "email@test.com")
pro := sqle.NewDoltDatabaseProvider()
session, err := dsess.NewSession(sql.NewEmptyContext(), enginetest.NewBaseSession(), pro, "test", "email@test.com")
require.NoError(t, err)
return &DoltHarness{
t: t,
@@ -118,7 +119,8 @@ func (d *DoltHarness) NewContext() *sql.Context {
}
func (d DoltHarness) NewSession() *sql.Context {
session, err := dsess.NewSession(sql.NewEmptyContext(), enginetest.NewBaseSession(), "test", "email@test.com")
pro := sqle.NewDoltDatabaseProvider()
session, err := dsess.NewSession(sql.NewEmptyContext(), enginetest.NewBaseSession(), pro, "test", "email@test.com")
require.NoError(d.t, err)
ctx := sql.NewContext(
@@ -159,7 +161,8 @@ func (d *DoltHarness) NewDatabases(names ...string) []sql.Database {
// the same name, the first write query will panic on dangling references in the noms layer. Not sure why this is
// happening, but it only happens as a result of this test setup.
var err error
d.session, err = dsess.NewSession(sql.NewEmptyContext(), enginetest.NewBaseSession(), "test", "email@test.com")
pro := sqle.NewDoltDatabaseProvider()
d.session, err = dsess.NewSession(sql.NewEmptyContext(), enginetest.NewBaseSession(), pro, "test", "email@test.com")
require.NoError(d.t, err)
var dbs []sql.Database