mirror of
https://github.com/dolthub/dolt.git
synced 2026-04-30 03:26:47 -05:00
provide dynamic databases
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user