github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/dsess/database_session_state.go (about)

     1  // Copyright 2020 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package dsess
    16  
    17  import (
    18  	"strings"
    19  
    20  	"github.com/dolthub/go-mysql-server/sql"
    21  
    22  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    23  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    24  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/globalstate"
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/writer"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/table/editor"
    27  	"github.com/dolthub/dolt/go/libraries/utils/concurrentmap"
    28  )
    29  
    30  // InitialDbState is the initial state of a database, as returned by SessionDatabase.InitialDBState. It is used to
    31  // establish the in memory state of the session for every new transaction.
    32  type InitialDbState struct {
    33  	Db sql.Database
    34  	// WorkingSet is the working set for this database. May be nil for databases tied to a detached root value, in which
    35  	// case HeadCommit must be set
    36  	WorkingSet *doltdb.WorkingSet
    37  	// The head commit for this database. May be nil for databases tied to a detached root value, in which case
    38  	// RootValue must be set.
    39  	HeadCommit *doltdb.Commit
    40  	// HeadRoot is the root value for databases without a HeadCommit. Nil for databases with a HeadCommit.
    41  	HeadRoot doltdb.RootValue
    42  	ReadOnly bool
    43  	DbData   env.DbData
    44  	Remotes  *concurrentmap.Map[string, env.Remote]
    45  	Branches *concurrentmap.Map[string, env.BranchConfig]
    46  	Backups  *concurrentmap.Map[string, env.Remote]
    47  
    48  	// If err is set, this InitialDbState is partially invalid, but may be
    49  	// usable to initialize a database at a revision specifier, for
    50  	// example. Adding this InitialDbState to a session will return this
    51  	// error.
    52  	Err error
    53  }
    54  
    55  // SessionDatabase is a database that can be managed by a dsess.Session. It has methods to return its initial state in
    56  // order for the session to manage it.
    57  type SessionDatabase interface {
    58  	sql.Database
    59  	InitialDBState(ctx *sql.Context) (InitialDbState, error)
    60  }
    61  
    62  // DatabaseSessionState is the set of all information for a given database in this session.
    63  type DatabaseSessionState struct {
    64  	// dbName is the name of the database this state applies to. This is always the base name of the database, without
    65  	// a revision qualifier.
    66  	dbName string
    67  	// checkedOutRevSpec is the revision of the database when referred to by its base name. Changes only when a
    68  	// `dolt_checkout` occurs.
    69  	checkedOutRevSpec string
    70  	// heads records the in-memory DB state for every branch head accessed by the session
    71  	heads map[string]*branchState
    72  	// headCache records the session-caches for every branch head accessed by the session
    73  	// This is managed separately from the branch states themselves because it persists across transactions (which is
    74  	// safe because it's keyed by immutable hashes)
    75  	headCache map[string]*SessionCache
    76  	// globalState is the global state of this session (shared by all sessions for a particular db)
    77  	globalState globalstate.GlobalState
    78  	// tmpFileDir is the directory to use for temporary files for this database
    79  	tmpFileDir string
    80  
    81  	// Same as InitialDbState.Err, this signifies that this
    82  	// DatabaseSessionState is invalid. LookupDbState returning a
    83  	// DatabaseSessionState with Err != nil will return that err.
    84  	Err error
    85  }
    86  
    87  func newEmptyDatabaseSessionState() *DatabaseSessionState {
    88  	return &DatabaseSessionState{
    89  		heads:     make(map[string]*branchState),
    90  		headCache: make(map[string]*SessionCache),
    91  	}
    92  }
    93  
    94  // SessionState is the public interface for dealing with session state outside this package. Session-state is always
    95  // branch-specific.
    96  type SessionState interface {
    97  	WorkingSet() *doltdb.WorkingSet
    98  	WorkingRoot() doltdb.RootValue
    99  	WriteSession() writer.WriteSession
   100  	EditOpts() editor.Options
   101  	SessionCache() *SessionCache
   102  }
   103  
   104  // branchState records all the in-memory session state for a particular branch head
   105  type branchState struct {
   106  	// dbState is the parent database state for this branch head state
   107  	dbState *DatabaseSessionState
   108  	// head is the name of the branch head for this state
   109  	head string
   110  	// revisionType is the type of revision this branchState tracks
   111  	revisionType RevisionType
   112  	// headCommit is the head commit for this database. May be nil for databases tied to a detached root value, in which
   113  	// case headRoot must be set.
   114  	headCommit *doltdb.Commit
   115  	// HeadRoot is the root value for databases without a headCommit. Nil for databases with a headCommit.
   116  	headRoot doltdb.RootValue
   117  	// workingSet is the working set for this database. May be nil for databases tied to a detached root value, in which
   118  	// case headCommit must be set
   119  	workingSet *doltdb.WorkingSet
   120  	// dbData is an accessor for the underlying doltDb
   121  	dbData env.DbData
   122  	// writeSession is this head's write session
   123  	writeSession writer.WriteSession
   124  	// readOnly is true if this database is read only
   125  	readOnly bool
   126  	// dirty is true if this branch state has uncommitted changes
   127  	dirty bool
   128  }
   129  
   130  // NewEmptyBranchState creates a new branch state for the given head name with the head provided, adds it to the db
   131  // state, and returns it. The state returned is empty except for its identifiers and must be filled in by the caller.
   132  func (dbState *DatabaseSessionState) NewEmptyBranchState(head string, revisionType RevisionType) *branchState {
   133  	b := &branchState{
   134  		dbState:      dbState,
   135  		head:         head,
   136  		revisionType: revisionType,
   137  	}
   138  
   139  	lowerHead := strings.ToLower(head)
   140  	dbState.heads[lowerHead] = b
   141  	_, ok := dbState.headCache[lowerHead]
   142  	if !ok {
   143  		dbState.headCache[lowerHead] = newSessionCache()
   144  	}
   145  
   146  	return b
   147  }
   148  
   149  // RevisionDbName returns the revision-qualified database name for this branch state
   150  func (bs *branchState) RevisionDbName() string {
   151  	return RevisionDbName(bs.dbState.dbName, bs.head)
   152  }
   153  
   154  func (bs *branchState) WorkingRoot() doltdb.RootValue {
   155  	return bs.roots().Working
   156  }
   157  
   158  var _ SessionState = (*branchState)(nil)
   159  
   160  func (bs *branchState) WorkingSet() *doltdb.WorkingSet {
   161  	return bs.workingSet
   162  }
   163  
   164  func (bs *branchState) WriteSession() writer.WriteSession {
   165  	return bs.writeSession
   166  }
   167  
   168  func (bs *branchState) SessionCache() *SessionCache {
   169  	return bs.dbState.headCache[strings.ToLower(bs.head)]
   170  }
   171  
   172  func (bs branchState) EditOpts() editor.Options {
   173  	if bs.writeSession == nil {
   174  		return editor.Options{}
   175  	}
   176  	return bs.WriteSession().GetOptions()
   177  }
   178  
   179  func (bs *branchState) roots() doltdb.Roots {
   180  	if bs.WorkingSet() == nil {
   181  		return doltdb.Roots{
   182  			Head:    bs.headRoot,
   183  			Working: bs.headRoot,
   184  			Staged:  bs.headRoot,
   185  		}
   186  	}
   187  	return doltdb.Roots{
   188  		Head:    bs.headRoot,
   189  		Working: bs.WorkingSet().WorkingRoot(),
   190  		Staged:  bs.WorkingSet().StagedRoot(),
   191  	}
   192  }