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

     1  // Copyright 2019-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 sqle
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"io"
    22  	"strings"
    23  	"time"
    24  
    25  	sqle "github.com/dolthub/go-mysql-server"
    26  	"github.com/dolthub/go-mysql-server/sql"
    27  	"github.com/dolthub/go-mysql-server/sql/analyzer"
    28  	"github.com/dolthub/go-mysql-server/sql/analyzer/analyzererrors"
    29  	"github.com/dolthub/go-mysql-server/sql/expression"
    30  	"github.com/dolthub/go-mysql-server/sql/fulltext"
    31  	"github.com/dolthub/go-mysql-server/sql/plan"
    32  	"github.com/dolthub/go-mysql-server/sql/planbuilder"
    33  	"github.com/dolthub/go-mysql-server/sql/rowexec"
    34  	"github.com/dolthub/go-mysql-server/sql/types"
    35  	"github.com/shopspring/decimal"
    36  	"gopkg.in/src-d/go-errors.v1"
    37  
    38  	"github.com/dolthub/dolt/go/libraries/doltcore/branch_control"
    39  	"github.com/dolthub/dolt/go/libraries/doltcore/diff"
    40  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    41  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    42  	"github.com/dolthub/dolt/go/libraries/doltcore/env/actions/commitwalk"
    43  	"github.com/dolthub/dolt/go/libraries/doltcore/rebase"
    44  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    45  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    46  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dprocedures"
    47  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
    48  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables"
    49  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/globalstate"
    50  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    51  	"github.com/dolthub/dolt/go/libraries/doltcore/table/editor"
    52  	"github.com/dolthub/dolt/go/libraries/utils/concurrentmap"
    53  	"github.com/dolthub/dolt/go/store/hash"
    54  	"github.com/dolthub/dolt/go/store/val"
    55  )
    56  
    57  var ErrInvalidTableName = errors.NewKind("Invalid table name %s.")
    58  var ErrReservedTableName = errors.NewKind("Invalid table name %s. Table names beginning with `dolt_` are reserved for internal use")
    59  var ErrReservedDiffTableName = errors.NewKind("Invalid table name %s. Table names beginning with `__DATABASE__` are reserved for internal use")
    60  var ErrSystemTableAlter = errors.NewKind("Cannot alter table %s: system tables cannot be dropped or altered")
    61  
    62  // Database implements sql.Database for a dolt DB.
    63  type Database struct {
    64  	baseName      string
    65  	requestedName string
    66  	schemaName    string
    67  	ddb           *doltdb.DoltDB
    68  	rsr           env.RepoStateReader
    69  	rsw           env.RepoStateWriter
    70  	gs            dsess.GlobalStateImpl
    71  	editOpts      editor.Options
    72  	revision      string
    73  	revType       dsess.RevisionType
    74  }
    75  
    76  var _ dsess.SqlDatabase = Database{}
    77  var _ dsess.RevisionDatabase = Database{}
    78  var _ globalstate.GlobalStateProvider = Database{}
    79  var _ sql.CollatedDatabase = Database{}
    80  var _ sql.Database = Database{}
    81  var _ sql.StoredProcedureDatabase = Database{}
    82  var _ sql.TableCreator = Database{}
    83  var _ sql.IndexedTableCreator = Database{}
    84  var _ sql.TableDropper = Database{}
    85  var _ sql.TableRenamer = Database{}
    86  var _ sql.TemporaryTableCreator = Database{}
    87  var _ sql.TemporaryTableDatabase = Database{}
    88  var _ sql.TriggerDatabase = Database{}
    89  var _ sql.VersionedDatabase = Database{}
    90  var _ sql.ViewDatabase = Database{}
    91  var _ sql.EventDatabase = Database{}
    92  var _ sql.AliasedDatabase = Database{}
    93  var _ fulltext.Database = Database{}
    94  var _ rebase.RebasePlanDatabase = Database{}
    95  var _ sql.SchemaValidator = Database{}
    96  var _ sql.SchemaDatabase = Database{}
    97  var _ sql.DatabaseSchema = Database{}
    98  
    99  type ReadOnlyDatabase struct {
   100  	Database
   101  }
   102  
   103  var _ sql.ReadOnlyDatabase = ReadOnlyDatabase{}
   104  var _ dsess.SqlDatabase = ReadOnlyDatabase{}
   105  
   106  func (r ReadOnlyDatabase) IsReadOnly() bool {
   107  	return true
   108  }
   109  
   110  func (r ReadOnlyDatabase) InitialDBState(ctx *sql.Context) (dsess.InitialDbState, error) {
   111  	return initialDBState(ctx, r, r.revision)
   112  }
   113  
   114  func (r ReadOnlyDatabase) WithBranchRevision(requestedName string, branchSpec dsess.SessionDatabaseBranchSpec) (dsess.SqlDatabase, error) {
   115  	revDb, err := r.Database.WithBranchRevision(requestedName, branchSpec)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	r.Database = revDb.(Database)
   121  	return r, nil
   122  }
   123  
   124  func (db Database) WithBranchRevision(requestedName string, branchSpec dsess.SessionDatabaseBranchSpec) (dsess.SqlDatabase, error) {
   125  	db.rsr, db.rsw = branchSpec.RepoState, branchSpec.RepoState
   126  	db.revision = branchSpec.Branch
   127  	db.revType = dsess.RevisionTypeBranch
   128  	db.requestedName = requestedName
   129  
   130  	return db, nil
   131  }
   132  
   133  func (db Database) ValidateSchema(sch sql.Schema) error {
   134  	if rowLen := schema.MaxRowStorageSize(sch); rowLen > int64(val.MaxTupleDataSize) {
   135  		// |val.MaxTupleDataSize| is less than |types.MaxRowLength| to account for
   136  		// serial message metadata
   137  		return analyzererrors.ErrInvalidRowLength.New(val.MaxTupleDataSize, rowLen)
   138  	}
   139  	return nil
   140  }
   141  
   142  // Revision implements dsess.RevisionDatabase
   143  func (db Database) Revision() string {
   144  	return db.revision
   145  }
   146  
   147  func (db Database) Versioned() bool {
   148  	return true
   149  }
   150  
   151  func (db Database) RevisionType() dsess.RevisionType {
   152  	return db.revType
   153  }
   154  
   155  func (db Database) EditOptions() editor.Options {
   156  	return db.editOpts
   157  }
   158  
   159  func (db Database) DoltDatabases() []*doltdb.DoltDB {
   160  	return []*doltdb.DoltDB{db.ddb}
   161  }
   162  
   163  // NewDatabase returns a new dolt database to use in queries.
   164  func NewDatabase(ctx context.Context, name string, dbData env.DbData, editOpts editor.Options) (Database, error) {
   165  	globalState, err := dsess.NewGlobalStateStoreForDb(ctx, name, dbData.Ddb)
   166  	if err != nil {
   167  		return Database{}, err
   168  	}
   169  
   170  	return Database{
   171  		baseName:      name,
   172  		requestedName: name,
   173  		ddb:           dbData.Ddb,
   174  		rsr:           dbData.Rsr,
   175  		rsw:           dbData.Rsw,
   176  		gs:            globalState,
   177  		editOpts:      editOpts,
   178  	}, nil
   179  }
   180  
   181  // initialDBState returns the InitialDbState for |db|. Other implementations of SqlDatabase outside this file should
   182  // implement their own method for an initial db state and not rely on this method.
   183  func initialDBState(ctx *sql.Context, db dsess.SqlDatabase, branch string) (dsess.InitialDbState, error) {
   184  	if len(db.Revision()) > 0 {
   185  		return initialStateForRevisionDb(ctx, db)
   186  	}
   187  
   188  	return initialDbState(ctx, db, branch)
   189  }
   190  
   191  func (db Database) InitialDBState(ctx *sql.Context) (dsess.InitialDbState, error) {
   192  	return initialDBState(ctx, db, db.revision)
   193  }
   194  
   195  // Name returns the name of this database, set at creation time.
   196  func (db Database) Name() string {
   197  	return db.RequestedName()
   198  }
   199  
   200  func (db Database) Schema() string {
   201  	return db.schemaName
   202  }
   203  
   204  // AliasedName is what allows databases named e.g. `mydb/b1` to work with the grant and info schema tables that expect
   205  // a base (no revision qualifier) db name
   206  func (db Database) AliasedName() string {
   207  	return db.baseName
   208  }
   209  
   210  // RevisionQualifiedName returns the name of this database including its revision qualifier, if any. This method should
   211  // be used whenever accessing internal state of a database and its tables.
   212  func (db Database) RevisionQualifiedName() string {
   213  	if db.revision == "" {
   214  		return db.baseName
   215  	}
   216  	return db.baseName + dsess.DbRevisionDelimiter + db.revision
   217  }
   218  
   219  func (db Database) RequestedName() string {
   220  	return db.requestedName
   221  }
   222  
   223  // GetDoltDB gets the underlying DoltDB of the Database
   224  func (db Database) GetDoltDB() *doltdb.DoltDB {
   225  	return db.ddb
   226  }
   227  
   228  // GetStateReader gets the RepoStateReader for a Database
   229  func (db Database) GetStateReader() env.RepoStateReader {
   230  	return db.rsr
   231  }
   232  
   233  // GetStateWriter gets the RepoStateWriter for a Database
   234  func (db Database) GetStateWriter() env.RepoStateWriter {
   235  	return db.rsw
   236  }
   237  
   238  func (db Database) DbData() env.DbData {
   239  	return env.DbData{
   240  		Ddb: db.ddb,
   241  		Rsw: db.rsw,
   242  		Rsr: db.rsr,
   243  	}
   244  }
   245  
   246  func (db Database) GetGlobalState() globalstate.GlobalState {
   247  	return db.gs
   248  }
   249  
   250  // GetTableInsensitive is used when resolving tables in queries. It returns a best-effort case-insensitive match for
   251  // the table name given.
   252  func (db Database) GetTableInsensitive(ctx *sql.Context, tblName string) (sql.Table, bool, error) {
   253  	// We start by first checking whether the input table is a temporary table. Temporary tables with name `x` take
   254  	// priority over persisted tables of name `x`.
   255  	ds := dsess.DSessFromSess(ctx.Session)
   256  	if tbl, ok := ds.GetTemporaryTable(ctx, db.Name(), tblName); ok {
   257  		return tbl, ok, nil
   258  	}
   259  
   260  	root, err := db.GetRoot(ctx)
   261  	if err != nil {
   262  		return nil, false, err
   263  	}
   264  
   265  	return db.getTableInsensitive(ctx, nil, ds, root, tblName, "")
   266  }
   267  
   268  // GetTableInsensitiveAsOf implements sql.VersionedDatabase
   269  func (db Database) GetTableInsensitiveAsOf(ctx *sql.Context, tableName string, asOf interface{}) (sql.Table, bool, error) {
   270  	if asOf == nil {
   271  		return db.GetTableInsensitive(ctx, tableName)
   272  	}
   273  	head, root, err := resolveAsOf(ctx, db, asOf)
   274  	if err != nil {
   275  		return nil, false, err
   276  	} else if root == nil {
   277  		return nil, false, nil
   278  	}
   279  
   280  	sess := dsess.DSessFromSess(ctx.Session)
   281  
   282  	table, ok, err := db.getTableInsensitive(ctx, head, sess, root, tableName, asOf)
   283  	if err != nil {
   284  		return nil, false, err
   285  	}
   286  	if !ok {
   287  		return nil, false, nil
   288  	}
   289  
   290  	if doltdb.IsReadOnlySystemTable(tableName) {
   291  		// currently, system tables do not need to be "locked to root"
   292  		//  see comment below in getTableInsensitive
   293  		return table, ok, nil
   294  	}
   295  
   296  	switch t := table.(type) {
   297  	case dtables.VersionableTable:
   298  		versionedTable, err := t.LockedToRoot(ctx, root)
   299  		if err != nil {
   300  			return nil, false, err
   301  		}
   302  		return versionedTable, true, nil
   303  
   304  	case *plan.EmptyTable:
   305  		// getTableInsensitive returns *plan.EmptyTable if the table doesn't exist in the data root, but
   306  		// schemas have been locked to a commit where the table does exist. Since the table is empty,
   307  		// there's no need to lock it to a root.
   308  		return t, true, nil
   309  
   310  	default:
   311  		return nil, false, fmt.Errorf("unexpected table type %T", table)
   312  	}
   313  
   314  }
   315  
   316  func (db Database) getTableInsensitive(ctx *sql.Context, head *doltdb.Commit, ds *dsess.DoltSession, root doltdb.RootValue, tblName string, asOf interface{}) (sql.Table, bool, error) {
   317  	lwrName := strings.ToLower(tblName)
   318  
   319  	// TODO: these tables that cache a root value at construction time should not, they need to get it from the session
   320  	//  at runtime
   321  	switch {
   322  	case strings.HasPrefix(lwrName, doltdb.DoltDiffTablePrefix):
   323  		if head == nil {
   324  			var err error
   325  			head, err = ds.GetHeadCommit(ctx, db.RevisionQualifiedName())
   326  
   327  			if err != nil {
   328  				return nil, false, err
   329  			}
   330  		}
   331  
   332  		tableName := tblName[len(doltdb.DoltDiffTablePrefix):]
   333  		dt, err := dtables.NewDiffTable(ctx, db.Name(), tableName, db.ddb, root, head)
   334  		if err != nil {
   335  			return nil, false, err
   336  		}
   337  		return dt, true, nil
   338  
   339  	case strings.HasPrefix(lwrName, doltdb.DoltCommitDiffTablePrefix):
   340  		suffix := tblName[len(doltdb.DoltCommitDiffTablePrefix):]
   341  		dt, err := dtables.NewCommitDiffTable(ctx, db.Name(), suffix, db.ddb, root)
   342  		if err != nil {
   343  			return nil, false, err
   344  		}
   345  		return dt, true, nil
   346  
   347  	case strings.HasPrefix(lwrName, doltdb.DoltHistoryTablePrefix):
   348  		baseTableName := tblName[len(doltdb.DoltHistoryTablePrefix):]
   349  		baseTable, ok, err := db.getTable(ctx, root, baseTableName)
   350  		if err != nil {
   351  			return nil, false, err
   352  		}
   353  		if !ok {
   354  			return nil, false, nil
   355  		}
   356  
   357  		if head == nil {
   358  			var err error
   359  			head, err = ds.GetHeadCommit(ctx, db.RevisionQualifiedName())
   360  			if err != nil {
   361  				return nil, false, err
   362  			}
   363  		}
   364  
   365  		switch t := baseTable.(type) {
   366  		case *AlterableDoltTable:
   367  			return NewHistoryTable(t.DoltTable, db.ddb, head), true, nil
   368  		case *WritableDoltTable:
   369  			return NewHistoryTable(t.DoltTable, db.ddb, head), true, nil
   370  		default:
   371  			return nil, false, fmt.Errorf("expected Alterable or WritableDoltTable, found %T", baseTable)
   372  		}
   373  
   374  	case strings.HasPrefix(lwrName, doltdb.DoltConfTablePrefix):
   375  		suffix := tblName[len(doltdb.DoltConfTablePrefix):]
   376  		srcTable, ok, err := db.getTableInsensitive(ctx, head, ds, root, suffix, asOf)
   377  		if err != nil {
   378  			return nil, false, err
   379  		} else if !ok {
   380  			return nil, false, nil
   381  		}
   382  		dt, err := dtables.NewConflictsTable(ctx, suffix, srcTable, root, dtables.RootSetter(db))
   383  		if err != nil {
   384  			return nil, false, err
   385  		}
   386  		return dt, true, nil
   387  
   388  	case strings.HasPrefix(lwrName, doltdb.DoltConstViolTablePrefix):
   389  		suffix := tblName[len(doltdb.DoltConstViolTablePrefix):]
   390  		dt, err := dtables.NewConstraintViolationsTable(ctx, suffix, root, dtables.RootSetter(db))
   391  		if err != nil {
   392  			return nil, false, err
   393  		}
   394  		return dt, true, nil
   395  	}
   396  
   397  	var dt sql.Table
   398  	found := false
   399  	switch lwrName {
   400  	case doltdb.LogTableName:
   401  		if head == nil {
   402  			var err error
   403  			head, err = ds.GetHeadCommit(ctx, db.RevisionQualifiedName())
   404  			if err != nil {
   405  				return nil, false, err
   406  			}
   407  		}
   408  
   409  		dt, found = dtables.NewLogTable(ctx, db.Name(), db.ddb, head), true
   410  	case doltdb.DiffTableName:
   411  		if head == nil {
   412  			var err error
   413  			head, err = ds.GetHeadCommit(ctx, db.RevisionQualifiedName())
   414  			if err != nil {
   415  				return nil, false, err
   416  			}
   417  		}
   418  
   419  		dt, found = dtables.NewUnscopedDiffTable(ctx, db.Name(), db.ddb, head), true
   420  	case doltdb.ColumnDiffTableName:
   421  		if head == nil {
   422  			var err error
   423  			head, err = ds.GetHeadCommit(ctx, db.RevisionQualifiedName())
   424  			if err != nil {
   425  				return nil, false, err
   426  			}
   427  		}
   428  
   429  		dt, found = dtables.NewColumnDiffTable(ctx, db.Name(), db.ddb, head), true
   430  	case doltdb.TableOfTablesInConflictName:
   431  		dt, found = dtables.NewTableOfTablesInConflict(ctx, db.RevisionQualifiedName(), db.ddb), true
   432  	case doltdb.TableOfTablesWithViolationsName:
   433  		dt, found = dtables.NewTableOfTablesConstraintViolations(ctx, root), true
   434  	case doltdb.SchemaConflictsTableName:
   435  		dt, found = dtables.NewSchemaConflictsTable(ctx, db.RevisionQualifiedName(), db.ddb), true
   436  	case doltdb.BranchesTableName:
   437  		dt, found = dtables.NewBranchesTable(ctx, db), true
   438  	case doltdb.RemoteBranchesTableName:
   439  		dt, found = dtables.NewRemoteBranchesTable(ctx, db), true
   440  	case doltdb.RemotesTableName:
   441  		dt, found = dtables.NewRemotesTable(ctx, db.ddb), true
   442  	case doltdb.CommitsTableName:
   443  		dt, found = dtables.NewCommitsTable(ctx, db.Name(), db.ddb), true
   444  	case doltdb.CommitAncestorsTableName:
   445  		dt, found = dtables.NewCommitAncestorsTable(ctx, db.Name(), db.ddb), true
   446  	case doltdb.StatusTableName:
   447  		sess := dsess.DSessFromSess(ctx.Session)
   448  		adapter := dsess.NewSessionStateAdapter(
   449  			sess, db.RevisionQualifiedName(),
   450  			concurrentmap.New[string, env.Remote](),
   451  			concurrentmap.New[string, env.BranchConfig](),
   452  			concurrentmap.New[string, env.Remote]())
   453  		ws, err := sess.WorkingSet(ctx, db.RevisionQualifiedName())
   454  		if err != nil {
   455  			return nil, false, err
   456  		}
   457  
   458  		dt, found = dtables.NewStatusTable(ctx, db.ddb, ws, adapter), true
   459  	case doltdb.MergeStatusTableName:
   460  		dt, found = dtables.NewMergeStatusTable(db.RevisionQualifiedName()), true
   461  	case doltdb.TagsTableName:
   462  		dt, found = dtables.NewTagsTable(ctx, db.ddb), true
   463  	case dtables.AccessTableName:
   464  		basCtx := branch_control.GetBranchAwareSession(ctx)
   465  		if basCtx != nil {
   466  			if controller := basCtx.GetController(); controller != nil {
   467  				dt, found = dtables.NewBranchControlTable(controller.Access), true
   468  			}
   469  		}
   470  	case dtables.NamespaceTableName:
   471  		basCtx := branch_control.GetBranchAwareSession(ctx)
   472  		if basCtx != nil {
   473  			if controller := basCtx.GetController(); controller != nil {
   474  				dt, found = dtables.NewBranchNamespaceControlTable(controller.Namespace), true
   475  			}
   476  		}
   477  	case doltdb.IgnoreTableName:
   478  		backingTable, _, err := db.getTable(ctx, root, doltdb.IgnoreTableName)
   479  		if err != nil {
   480  			return nil, false, err
   481  		}
   482  		if backingTable == nil {
   483  			dt, found = dtables.NewEmptyIgnoreTable(ctx), true
   484  		} else {
   485  			versionableTable := backingTable.(dtables.VersionableTable)
   486  			dt, found = dtables.NewIgnoreTable(ctx, versionableTable), true
   487  		}
   488  	case doltdb.DocTableName:
   489  		backingTable, _, err := db.getTable(ctx, root, doltdb.DocTableName)
   490  		if err != nil {
   491  			return nil, false, err
   492  		}
   493  		if backingTable == nil {
   494  			dt, found = dtables.NewEmptyDocsTable(ctx), true
   495  		} else {
   496  			versionableTable := backingTable.(dtables.VersionableTable)
   497  			dt, found = dtables.NewDocsTable(ctx, versionableTable), true
   498  		}
   499  	case doltdb.StatisticsTableName:
   500  		dt, found = dtables.NewStatisticsTable(ctx, db.Name(), db.ddb, asOf), true
   501  	}
   502  
   503  	if found {
   504  		return dt, found, nil
   505  	}
   506  
   507  	// TODO: this should reuse the root, not lookup the db state again
   508  	table, found, err := db.getTable(ctx, root, tblName)
   509  	if err != nil {
   510  		return nil, false, err
   511  	}
   512  	if found {
   513  		return table, found, err
   514  	}
   515  
   516  	// If the table wasn't found in the specified data root, check if there is an overridden
   517  	// schema commit that contains it and return an empty table if so.
   518  	return resolveOverriddenNonexistentTable(ctx, tblName, db)
   519  }
   520  
   521  // resolveAsOf resolves given expression to a commit, if one exists.
   522  func resolveAsOf(ctx *sql.Context, db Database, asOf interface{}) (*doltdb.Commit, doltdb.RootValue, error) {
   523  	head, err := db.rsr.CWBHeadRef()
   524  	if err != nil {
   525  		return nil, nil, err
   526  	}
   527  	switch x := asOf.(type) {
   528  	case time.Time:
   529  		return resolveAsOfTime(ctx, db.ddb, head, x)
   530  	case string:
   531  		return resolveAsOfCommitRef(ctx, db, head, x)
   532  	default:
   533  		return nil, nil, fmt.Errorf("unsupported AS OF type %T", asOf)
   534  	}
   535  }
   536  
   537  func resolveAsOfTime(ctx *sql.Context, ddb *doltdb.DoltDB, head ref.DoltRef, asOf time.Time) (*doltdb.Commit, doltdb.RootValue, error) {
   538  	cs, err := doltdb.NewCommitSpec("HEAD")
   539  	if err != nil {
   540  		return nil, nil, err
   541  	}
   542  
   543  	optCmt, err := ddb.Resolve(ctx, cs, head)
   544  	if err != nil {
   545  		return nil, nil, err
   546  	}
   547  	cm, ok := optCmt.ToCommit()
   548  	if !ok {
   549  		return nil, nil, doltdb.ErrGhostCommitEncountered
   550  	}
   551  
   552  	h, err := cm.HashOf()
   553  	if err != nil {
   554  		return nil, nil, err
   555  	}
   556  
   557  	cmItr, err := commitwalk.GetTopologicalOrderIterator(ctx, ddb, []hash.Hash{h}, nil)
   558  	if err != nil {
   559  		return nil, nil, err
   560  	}
   561  
   562  	for {
   563  		_, optCmt, err := cmItr.Next(ctx)
   564  		if err == io.EOF {
   565  			break
   566  		} else if err != nil {
   567  			return nil, nil, err
   568  		}
   569  		curr, ok := optCmt.ToCommit()
   570  		if !ok {
   571  			return nil, nil, doltdb.ErrGhostCommitEncountered
   572  		}
   573  
   574  		meta, err := curr.GetCommitMeta(ctx)
   575  		if err != nil {
   576  			return nil, nil, err
   577  		}
   578  
   579  		if meta.Time().Equal(asOf) || meta.Time().Before(asOf) {
   580  			root, err := curr.GetRootValue(ctx)
   581  			if err != nil {
   582  				return nil, nil, err
   583  			}
   584  			return curr, root, nil
   585  		}
   586  	}
   587  	return nil, nil, nil
   588  }
   589  
   590  func resolveAsOfCommitRef(ctx *sql.Context, db Database, head ref.DoltRef, commitRef string) (*doltdb.Commit, doltdb.RootValue, error) {
   591  	ddb := db.ddb
   592  
   593  	if commitRef == doltdb.Working || commitRef == doltdb.Staged {
   594  		sess := dsess.DSessFromSess(ctx.Session)
   595  		root, _, _, err := sess.ResolveRootForRef(ctx, ctx.GetCurrentDatabase(), commitRef)
   596  		if err != nil {
   597  			return nil, nil, err
   598  		}
   599  
   600  		cm, err := ddb.ResolveCommitRef(ctx, head)
   601  		if err != nil {
   602  			return nil, nil, err
   603  		}
   604  		return cm, root, nil
   605  	}
   606  
   607  	cs, err := doltdb.NewCommitSpec(commitRef)
   608  
   609  	if err != nil {
   610  		return nil, nil, err
   611  	}
   612  
   613  	nomsRoot, err := dsess.TransactionRoot(ctx, db)
   614  	if err != nil {
   615  		return nil, nil, err
   616  	}
   617  
   618  	optCmt, err := ddb.ResolveByNomsRoot(ctx, cs, head, nomsRoot)
   619  	if err != nil {
   620  		return nil, nil, err
   621  	}
   622  	cm, ok := optCmt.ToCommit()
   623  	if !ok {
   624  		return nil, nil, doltdb.ErrGhostCommitEncountered
   625  	}
   626  
   627  	root, err := cm.GetRootValue(ctx)
   628  	if err != nil {
   629  		return nil, nil, err
   630  	}
   631  
   632  	return cm, root, nil
   633  }
   634  
   635  // GetTableNamesAsOf implements sql.VersionedDatabase
   636  func (db Database) GetTableNamesAsOf(ctx *sql.Context, time interface{}) ([]string, error) {
   637  	_, root, err := resolveAsOf(ctx, db, time)
   638  	if err != nil {
   639  		return nil, err
   640  	} else if root == nil {
   641  		return nil, nil
   642  	}
   643  
   644  	tblNames, err := root.GetTableNames(ctx, doltdb.DefaultSchemaName)
   645  	if err != nil {
   646  		return nil, err
   647  	}
   648  	return filterDoltInternalTables(tblNames), nil
   649  }
   650  
   651  // getTable returns the user table with the given baseName from the root given
   652  func (db Database) getTable(ctx *sql.Context, root doltdb.RootValue, tableName string) (sql.Table, bool, error) {
   653  	sess := dsess.DSessFromSess(ctx.Session)
   654  	dbState, ok, err := sess.LookupDbState(ctx, db.RevisionQualifiedName())
   655  	if err != nil {
   656  		return nil, false, err
   657  	}
   658  	if !ok {
   659  		return nil, false, fmt.Errorf("no state for database %s", db.RevisionQualifiedName())
   660  	}
   661  
   662  	overriddenSchemaRoot, err := resolveOverriddenSchemaRoot(ctx, db)
   663  	if err != nil {
   664  		return nil, false, err
   665  	}
   666  
   667  	// If schema hasn't been overridden, we can use a cached table if one exists
   668  	if overriddenSchemaRoot == nil {
   669  		key, err := doltdb.NewDataCacheKey(root)
   670  		if err != nil {
   671  			return nil, false, err
   672  		}
   673  
   674  		cachedTable, ok := dbState.SessionCache().GetCachedTable(key, dsess.TableCacheKey{Name: tableName, Schema: db.schemaName})
   675  		if ok {
   676  			return cachedTable, true, nil
   677  		}
   678  	}
   679  
   680  	tableNames, err := db.getAllTableNames(ctx, root)
   681  	if err != nil {
   682  		return nil, true, err
   683  	}
   684  
   685  	tableName, ok = sql.GetTableNameInsensitive(tableName, tableNames)
   686  	if !ok {
   687  		return nil, false, nil
   688  	}
   689  
   690  	// TODO: should we short-circuit the schema name for system tables?
   691  	tbl, ok, err := root.GetTable(ctx, doltdb.TableName{Name: tableName, Schema: db.schemaName})
   692  	if err != nil {
   693  		return nil, false, err
   694  	} else if !ok {
   695  		// Should be impossible
   696  		return nil, false, doltdb.ErrTableNotFound
   697  	}
   698  
   699  	sch, err := tbl.GetSchema(ctx)
   700  	if err != nil {
   701  		return nil, false, err
   702  	}
   703  
   704  	if overriddenSchemaRoot != nil {
   705  		err = overrideSchemaForTable(ctx, tableName, tbl, overriddenSchemaRoot)
   706  		if err != nil {
   707  			return nil, false, err
   708  		}
   709  	}
   710  
   711  	table, err := db.newDoltTable(tableName, sch, tbl)
   712  	if err != nil {
   713  		return nil, false, err
   714  	}
   715  
   716  	// If the schema hasn't been overridden, cache the table
   717  	if overriddenSchemaRoot == nil {
   718  		key, err := doltdb.NewDataCacheKey(root)
   719  		if err != nil {
   720  			return nil, false, err
   721  		}
   722  		dbState.SessionCache().CacheTable(key, dsess.TableCacheKey{Name: tableName, Schema: db.schemaName}, table)
   723  	}
   724  
   725  	return table, true, nil
   726  }
   727  
   728  // newDoltTable returns a sql.Table wrapping the given underlying dolt table
   729  func (db Database) newDoltTable(tableName string, sch schema.Schema, tbl *doltdb.Table) (sql.Table, error) {
   730  	readonlyTable, err := NewDoltTable(tableName, sch, tbl, db, db.editOpts)
   731  	if err != nil {
   732  		return nil, err
   733  	}
   734  	var table sql.Table
   735  	if doltdb.IsReadOnlySystemTable(tableName) {
   736  		table = readonlyTable
   737  	} else if doltdb.HasDoltPrefix(tableName) && !doltdb.IsFullTextTable(tableName) {
   738  		table = &WritableDoltTable{DoltTable: readonlyTable, db: db}
   739  	} else {
   740  		table = &AlterableDoltTable{WritableDoltTable{DoltTable: readonlyTable, db: db}}
   741  	}
   742  
   743  	return table, nil
   744  }
   745  
   746  // GetTableNames returns the names of all user tables. System tables in user space (e.g. dolt_docs, dolt_query_catalog)
   747  // are filtered out. This method is used for queries that examine the schema of the database, e.g. show tables. Table
   748  // name resolution in queries is handled by GetTableInsensitive. Use GetAllTableNames for an unfiltered list of all
   749  // tables in user space.
   750  func (db Database) GetTableNames(ctx *sql.Context) ([]string, error) {
   751  	tblNames, err := db.GetAllTableNames(ctx)
   752  	if err != nil {
   753  		return nil, err
   754  	}
   755  	showSystemTables, err := ctx.GetSessionVariable(ctx, dsess.ShowSystemTables)
   756  	if err != nil {
   757  		return nil, err
   758  	}
   759  	if showSystemTables.(int8) == 1 {
   760  		return tblNames, nil
   761  	} else {
   762  		return filterDoltInternalTables(tblNames), nil
   763  	}
   764  }
   765  
   766  // GetAllTableNames returns all user-space tables, including system tables in user space
   767  // (e.g. dolt_docs, dolt_query_catalog).
   768  func (db Database) GetAllTableNames(ctx *sql.Context) ([]string, error) {
   769  	root, err := db.GetRoot(ctx)
   770  
   771  	if err != nil {
   772  		return nil, err
   773  	}
   774  
   775  	return db.getAllTableNames(ctx, root)
   776  }
   777  
   778  func (db Database) getAllTableNames(ctx context.Context, root doltdb.RootValue) ([]string, error) {
   779  	systemTables, err := doltdb.GetGeneratedSystemTables(ctx, root)
   780  	if err != nil {
   781  		return nil, err
   782  	}
   783  	result, err := root.GetTableNames(ctx, db.schemaName)
   784  	result = append(result, systemTables...)
   785  	return result, err
   786  }
   787  
   788  func filterDoltInternalTables(tblNames []string) []string {
   789  	result := []string{}
   790  	for _, tbl := range tblNames {
   791  		if !doltdb.HasDoltPrefix(tbl) {
   792  			result = append(result, tbl)
   793  		}
   794  	}
   795  	return result
   796  }
   797  
   798  // GetRoot returns the root value for this database session
   799  func (db Database) GetRoot(ctx *sql.Context) (doltdb.RootValue, error) {
   800  	sess := dsess.DSessFromSess(ctx.Session)
   801  	dbState, ok, err := sess.LookupDbState(ctx, db.RevisionQualifiedName())
   802  	if err != nil {
   803  		return nil, err
   804  	}
   805  	if !ok {
   806  		return nil, fmt.Errorf("no root value found in session")
   807  	}
   808  
   809  	return dbState.WorkingRoot(), nil
   810  }
   811  
   812  // GetWorkingSet gets the current working set for the database.
   813  // If there is no working set (most likely because the DB is in Detached Head mode, return an error.
   814  // If a command needs to work while in Detached Head, that command should call sess.LookupDbState directly.
   815  // TODO: This is a temporary measure to make sure that new commands that call GetWorkingSet don't unexpectedly receive
   816  // a null pointer. In the future, we should replace all uses of dbState.WorkingSet, including this, with a new interface
   817  // where users avoid handling the WorkingSet directly.
   818  func (db Database) GetWorkingSet(ctx *sql.Context) (*doltdb.WorkingSet, error) {
   819  	sess := dsess.DSessFromSess(ctx.Session)
   820  	dbState, ok, err := sess.LookupDbState(ctx, db.RevisionQualifiedName())
   821  	if err != nil {
   822  		return nil, err
   823  	}
   824  	if !ok {
   825  		return nil, fmt.Errorf("no root value found in session")
   826  	}
   827  	if dbState.WorkingSet() == nil {
   828  		return nil, doltdb.ErrOperationNotSupportedInDetachedHead
   829  	}
   830  	return dbState.WorkingSet(), nil
   831  }
   832  
   833  // SetRoot should typically be called on the Session, which is where this state lives. But it's available here as a
   834  // convenience.
   835  func (db Database) SetRoot(ctx *sql.Context, newRoot doltdb.RootValue) error {
   836  	sess := dsess.DSessFromSess(ctx.Session)
   837  	return sess.SetWorkingRoot(ctx, db.RevisionQualifiedName(), newRoot)
   838  }
   839  
   840  // GetHeadRoot returns root value for the current session head
   841  func (db Database) GetHeadRoot(ctx *sql.Context) (doltdb.RootValue, error) {
   842  	sess := dsess.DSessFromSess(ctx.Session)
   843  	head, err := sess.GetHeadCommit(ctx, db.RevisionQualifiedName())
   844  	if err != nil {
   845  		return nil, err
   846  	}
   847  	return head.GetRootValue(ctx)
   848  }
   849  
   850  // DropTable drops the table with the name given.
   851  // The planner returns the correct case sensitive name in tableName
   852  func (db Database) DropTable(ctx *sql.Context, tableName string) error {
   853  	if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Write); err != nil {
   854  		return err
   855  	}
   856  	if doltdb.IsNonAlterableSystemTable(tableName) {
   857  		return ErrSystemTableAlter.New(tableName)
   858  	}
   859  
   860  	return db.dropTable(ctx, tableName)
   861  }
   862  
   863  // dropTable drops the table with the baseName given, without any business logic checks
   864  func (db Database) dropTable(ctx *sql.Context, tableName string) error {
   865  	ds := dsess.DSessFromSess(ctx.Session)
   866  	if _, ok := ds.GetTemporaryTable(ctx, db.Name(), tableName); ok {
   867  		ds.DropTemporaryTable(ctx, db.Name(), tableName)
   868  		return nil
   869  	}
   870  
   871  	ws, err := db.GetWorkingSet(ctx)
   872  	if err != nil {
   873  		return err
   874  	}
   875  
   876  	root := ws.WorkingRoot()
   877  	tbl, tableExists, err := root.GetTable(ctx, doltdb.TableName{Name: tableName})
   878  	if err != nil {
   879  		return err
   880  	}
   881  
   882  	if !tableExists {
   883  		return sql.ErrTableNotFound.New(tableName)
   884  	}
   885  
   886  	newRoot, err := root.RemoveTables(ctx, true, false, tableName)
   887  	if err != nil {
   888  		return err
   889  	}
   890  
   891  	sch, err := tbl.GetSchema(ctx)
   892  	if err != nil {
   893  		return err
   894  	}
   895  
   896  	if schema.HasAutoIncrement(sch) {
   897  		ddb, _ := ds.GetDoltDB(ctx, db.RevisionQualifiedName())
   898  		err = db.removeTableFromAutoIncrementTracker(ctx, tableName, ddb, ws.Ref())
   899  		if err != nil {
   900  			return err
   901  		}
   902  	}
   903  
   904  	return db.SetRoot(ctx, newRoot)
   905  }
   906  
   907  // removeTableFromAutoIncrementTracker updates the global auto increment tracking as necessary to deal with the table
   908  // given being dropped or truncated. The auto increment value for this table after this operation will either be reset
   909  // back to 1 if this table only exists in the working set given, or to the highest value in all other working sets
   910  // otherwise. This operation is expensive if the
   911  func (db Database) removeTableFromAutoIncrementTracker(
   912  	ctx *sql.Context,
   913  	tableName string,
   914  	ddb *doltdb.DoltDB,
   915  	ws ref.WorkingSetRef,
   916  ) error {
   917  	branches, err := ddb.GetBranches(ctx)
   918  	if err != nil {
   919  		return err
   920  	}
   921  
   922  	var wses []*doltdb.WorkingSet
   923  	for _, b := range branches {
   924  		wsRef, err := ref.WorkingSetRefForHead(b)
   925  		if err != nil {
   926  			return err
   927  		}
   928  
   929  		if wsRef == ws {
   930  			// skip this branch, we've deleted it here
   931  			continue
   932  		}
   933  
   934  		ws, err := ddb.ResolveWorkingSet(ctx, wsRef)
   935  		if err == doltdb.ErrWorkingSetNotFound {
   936  			// skip, continue working on other branches
   937  			continue
   938  		} else if err != nil {
   939  			return err
   940  		}
   941  
   942  		wses = append(wses, ws)
   943  	}
   944  
   945  	ait, err := db.gs.AutoIncrementTracker(ctx)
   946  	if err != nil {
   947  		return err
   948  	}
   949  
   950  	err = ait.DropTable(ctx, tableName, wses...)
   951  	if err != nil {
   952  		return err
   953  	}
   954  
   955  	return nil
   956  }
   957  
   958  // CreateTable creates a table with the name and schema given.
   959  func (db Database) CreateTable(ctx *sql.Context, tableName string, sch sql.PrimaryKeySchema, collation sql.CollationID, comment string) error {
   960  	if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Write); err != nil {
   961  		return err
   962  	}
   963  
   964  	if doltdb.HasDoltPrefix(tableName) && !doltdb.IsFullTextTable(tableName) {
   965  		return ErrReservedTableName.New(tableName)
   966  	}
   967  
   968  	if strings.HasPrefix(tableName, diff.DBPrefix) {
   969  		return ErrReservedDiffTableName.New(tableName)
   970  	}
   971  
   972  	if !doltdb.IsValidTableName(tableName) {
   973  		return ErrInvalidTableName.New(tableName)
   974  	}
   975  
   976  	return db.createSqlTable(ctx, tableName, db.schemaName, sch, collation, comment)
   977  }
   978  
   979  // CreateIndexedTable creates a table with the name and schema given.
   980  func (db Database) CreateIndexedTable(ctx *sql.Context, tableName string, sch sql.PrimaryKeySchema, idxDef sql.IndexDef, collation sql.CollationID) error {
   981  	if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Write); err != nil {
   982  		return err
   983  	}
   984  
   985  	if doltdb.HasDoltPrefix(tableName) {
   986  		return ErrReservedTableName.New(tableName)
   987  	}
   988  
   989  	if strings.HasPrefix(tableName, diff.DBPrefix) {
   990  		return ErrReservedDiffTableName.New(tableName)
   991  	}
   992  
   993  	if !doltdb.IsValidTableName(tableName) {
   994  		return ErrInvalidTableName.New(tableName)
   995  	}
   996  
   997  	return db.createIndexedSqlTable(ctx, tableName, db.schemaName, sch, idxDef, collation)
   998  }
   999  
  1000  // CreateFulltextTableNames returns a set of names that will be used to create Full-Text pseudo-index tables.
  1001  func (db Database) CreateFulltextTableNames(ctx *sql.Context, parentTableName string, parentIndexName string) (fulltext.IndexTableNames, error) {
  1002  	allTableNames, err := db.GetAllTableNames(ctx)
  1003  	if err != nil {
  1004  		return fulltext.IndexTableNames{}, err
  1005  	}
  1006  	var tablePrefix string
  1007  OuterLoop:
  1008  	for i := uint64(0); true; i++ {
  1009  		tablePrefix = strings.ToLower(fmt.Sprintf("dolt_%s_%s_%d", parentTableName, parentIndexName, i))
  1010  		for _, tableName := range allTableNames {
  1011  			if strings.HasPrefix(strings.ToLower(tableName), tablePrefix) {
  1012  				continue OuterLoop
  1013  			}
  1014  		}
  1015  		break
  1016  	}
  1017  	return fulltext.IndexTableNames{
  1018  		Config:      fmt.Sprintf("dolt_%s_fts_config", parentTableName),
  1019  		Position:    fmt.Sprintf("%s_fts_position", tablePrefix),
  1020  		DocCount:    fmt.Sprintf("%s_fts_doc_count", tablePrefix),
  1021  		GlobalCount: fmt.Sprintf("%s_fts_global_count", tablePrefix),
  1022  		RowCount:    fmt.Sprintf("%s_fts_row_count", tablePrefix),
  1023  	}, nil
  1024  }
  1025  
  1026  // createSqlTable is the private version of CreateTable. It doesn't enforce any table name checks.
  1027  func (db Database) createSqlTable(ctx *sql.Context, tableName string, schemaName string, sch sql.PrimaryKeySchema, collation sql.CollationID, comment string) error {
  1028  	ws, err := db.GetWorkingSet(ctx)
  1029  	if err != nil {
  1030  		return err
  1031  	}
  1032  	root := ws.WorkingRoot()
  1033  
  1034  	// TODO: enforce that schema exists (redundant with other checks)
  1035  
  1036  	if exists, err := root.HasTable(ctx, tableName); err != nil {
  1037  		return err
  1038  	} else if exists {
  1039  		return sql.ErrTableAlreadyExists.New(tableName)
  1040  	}
  1041  
  1042  	headRoot, err := db.GetHeadRoot(ctx)
  1043  	if err != nil {
  1044  		return err
  1045  	}
  1046  
  1047  	doltSch, err := sqlutil.ToDoltSchema(ctx, root, tableName, sch, headRoot, collation)
  1048  	if err != nil {
  1049  		return err
  1050  	}
  1051  	doltSch.SetComment(comment)
  1052  
  1053  	// Prevent any tables that use Spatial Types as Primary Key from being created
  1054  	if schema.IsUsingSpatialColAsKey(doltSch) {
  1055  		return schema.ErrUsingSpatialKey.New(tableName)
  1056  	}
  1057  
  1058  	// Prevent any tables that use BINARY, CHAR, VARBINARY, VARCHAR prefixes
  1059  
  1060  	if schema.HasAutoIncrement(doltSch) {
  1061  		ait, err := db.gs.AutoIncrementTracker(ctx)
  1062  		if err != nil {
  1063  			return err
  1064  		}
  1065  		ait.AddNewTable(tableName)
  1066  	}
  1067  
  1068  	return db.createDoltTable(ctx, tableName, schemaName, root, doltSch)
  1069  }
  1070  
  1071  func hasDatabaseSchema(ctx context.Context, root doltdb.RootValue, schemaName string) (bool, error) {
  1072  	schemas, err := root.GetDatabaseSchemas(ctx)
  1073  	if err != nil {
  1074  		return false, err
  1075  	}
  1076  
  1077  	for _, schema := range schemas {
  1078  		if strings.EqualFold(schema.Name, schemaName) {
  1079  			return true, nil
  1080  		}
  1081  	}
  1082  
  1083  	return false, nil
  1084  }
  1085  
  1086  // createIndexedSqlTable is the private version of createSqlTable. It doesn't enforce any table name checks.
  1087  func (db Database) createIndexedSqlTable(ctx *sql.Context, tableName string, schemaName string, sch sql.PrimaryKeySchema, idxDef sql.IndexDef, collation sql.CollationID) error {
  1088  	ws, err := db.GetWorkingSet(ctx)
  1089  	if err != nil {
  1090  		return err
  1091  	}
  1092  	root := ws.WorkingRoot()
  1093  
  1094  	if exists, err := root.HasTable(ctx, tableName); err != nil {
  1095  		return err
  1096  	} else if exists {
  1097  		return sql.ErrTableAlreadyExists.New(tableName)
  1098  	}
  1099  
  1100  	headRoot, err := db.GetHeadRoot(ctx)
  1101  	if err != nil {
  1102  		return err
  1103  	}
  1104  
  1105  	doltSch, err := sqlutil.ToDoltSchema(ctx, root, tableName, sch, headRoot, collation)
  1106  	if err != nil {
  1107  		return err
  1108  	}
  1109  
  1110  	// Prevent any tables that use Spatial Types as Primary Key from being created
  1111  	if schema.IsUsingSpatialColAsKey(doltSch) {
  1112  		return schema.ErrUsingSpatialKey.New(tableName)
  1113  	}
  1114  
  1115  	// Prevent any tables that use BINARY, CHAR, VARBINARY, VARCHAR prefixes in Primary Key
  1116  	for _, idxCol := range idxDef.Columns {
  1117  		col := sch.Schema[sch.Schema.IndexOfColName(idxCol.Name)]
  1118  		if col.PrimaryKey && types.IsText(col.Type) && idxCol.Length > 0 {
  1119  			return sql.ErrUnsupportedIndexPrefix.New(col.Name)
  1120  		}
  1121  	}
  1122  
  1123  	if schema.HasAutoIncrement(doltSch) {
  1124  		ait, err := db.gs.AutoIncrementTracker(ctx)
  1125  		if err != nil {
  1126  			return err
  1127  		}
  1128  		ait.AddNewTable(tableName)
  1129  	}
  1130  
  1131  	return db.createDoltTable(ctx, tableName, schemaName, root, doltSch)
  1132  }
  1133  
  1134  // createDoltTable creates a table on the database using the given dolt schema while not enforcing table baseName checks.
  1135  func (db Database) createDoltTable(ctx *sql.Context, tableName string, schemaName string, root doltdb.RootValue, doltSch schema.Schema) error {
  1136  	if exists, err := root.HasTable(ctx, tableName); err != nil {
  1137  		return err
  1138  	} else if exists {
  1139  		return sql.ErrTableAlreadyExists.New(tableName)
  1140  	}
  1141  
  1142  	var conflictingTbls []string
  1143  	_ = doltSch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) {
  1144  		_, oldTableName, exists, err := doltdb.GetTableByColTag(ctx, root, tag)
  1145  		if err != nil {
  1146  			return true, err
  1147  		}
  1148  		if exists && oldTableName != tableName {
  1149  			errStr := schema.ErrTagPrevUsed(tag, col.Name, tableName, oldTableName).Error()
  1150  			conflictingTbls = append(conflictingTbls, errStr)
  1151  		}
  1152  		return false, nil
  1153  	})
  1154  
  1155  	if len(conflictingTbls) > 0 {
  1156  		return fmt.Errorf(strings.Join(conflictingTbls, "\n"))
  1157  	}
  1158  
  1159  	newRoot, err := doltdb.CreateEmptyTable(ctx, root, doltdb.TableName{Name: tableName, Schema: schemaName}, doltSch)
  1160  	if err != nil {
  1161  		return err
  1162  	}
  1163  
  1164  	return db.SetRoot(ctx, newRoot)
  1165  }
  1166  
  1167  // CreateTemporaryTable creates a table that only exists the length of a session.
  1168  func (db Database) CreateTemporaryTable(ctx *sql.Context, tableName string, pkSch sql.PrimaryKeySchema, collation sql.CollationID) error {
  1169  	if doltdb.HasDoltPrefix(tableName) {
  1170  		return ErrReservedTableName.New(tableName)
  1171  	}
  1172  
  1173  	if strings.HasPrefix(tableName, diff.DBPrefix) {
  1174  		return ErrReservedDiffTableName.New(tableName)
  1175  	}
  1176  
  1177  	if !doltdb.IsValidTableName(tableName) {
  1178  		return ErrInvalidTableName.New(tableName)
  1179  	}
  1180  
  1181  	tmp, err := NewTempTable(ctx, db.ddb, pkSch, tableName, db.Name(), db.editOpts, collation)
  1182  	if err != nil {
  1183  		return err
  1184  	}
  1185  
  1186  	ds := dsess.DSessFromSess(ctx.Session)
  1187  	ds.AddTemporaryTable(ctx, db.Name(), tmp)
  1188  	return nil
  1189  }
  1190  
  1191  // CreateSchema implements sql.SchemaDatabase
  1192  func (db Database) CreateSchema(ctx *sql.Context, schemaName string) error {
  1193  	if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Write); err != nil {
  1194  		return err
  1195  	}
  1196  	root, err := db.GetRoot(ctx)
  1197  	if err != nil {
  1198  		return err
  1199  	}
  1200  
  1201  	root, err = root.CreateDatabaseSchema(ctx, schema.DatabaseSchema{
  1202  		Name: schemaName,
  1203  	})
  1204  	if err != nil {
  1205  		return err
  1206  	}
  1207  
  1208  	return db.SetRoot(ctx, root)
  1209  }
  1210  
  1211  // GetSchema implements sql.SchemaDatabase
  1212  func (db Database) GetSchema(ctx *sql.Context, schemaName string) (sql.DatabaseSchema, bool, error) {
  1213  	ws, err := db.GetWorkingSet(ctx)
  1214  	if err != nil {
  1215  		return nil, false, err
  1216  	}
  1217  	root := ws.WorkingRoot()
  1218  
  1219  	schemas, err := root.GetDatabaseSchemas(ctx)
  1220  	if err != nil {
  1221  		return nil, false, err
  1222  	}
  1223  
  1224  	for _, schema := range schemas {
  1225  		if strings.EqualFold(schema.Name, schemaName) {
  1226  			db.schemaName = schema.Name
  1227  			return db, true, nil
  1228  		}
  1229  	}
  1230  
  1231  	// For a temporary backwards compatibility solution, always pretend the public schema exists.
  1232  	// Should create it explicitly when we create a new db in future.
  1233  	if strings.EqualFold(schemaName, "public") {
  1234  		db.schemaName = "public"
  1235  		return db, true, nil
  1236  	}
  1237  
  1238  	return nil, false, nil
  1239  }
  1240  
  1241  // AllSchemas implements sql.SchemaDatabase
  1242  func (db Database) AllSchemas(ctx *sql.Context) ([]sql.DatabaseSchema, error) {
  1243  	db.schemaName = "public"
  1244  	return []sql.DatabaseSchema{db}, nil
  1245  }
  1246  
  1247  // RenameTable implements sql.TableRenamer
  1248  func (db Database) RenameTable(ctx *sql.Context, oldName, newName string) error {
  1249  	if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Write); err != nil {
  1250  		return err
  1251  	}
  1252  	root, err := db.GetRoot(ctx)
  1253  
  1254  	if err != nil {
  1255  		return err
  1256  	}
  1257  
  1258  	if doltdb.IsNonAlterableSystemTable(oldName) {
  1259  		return ErrSystemTableAlter.New(oldName)
  1260  	}
  1261  
  1262  	if doltdb.HasDoltPrefix(newName) {
  1263  		return ErrReservedTableName.New(newName)
  1264  	}
  1265  
  1266  	if strings.HasPrefix(newName, diff.DBPrefix) {
  1267  		return ErrReservedDiffTableName.New(newName)
  1268  	}
  1269  
  1270  	if !doltdb.IsValidTableName(newName) {
  1271  		return ErrInvalidTableName.New(newName)
  1272  	}
  1273  
  1274  	if _, ok, _ := db.GetTableInsensitive(ctx, newName); ok {
  1275  		return sql.ErrTableAlreadyExists.New(newName)
  1276  	}
  1277  
  1278  	newRoot, err := renameTable(ctx, root, oldName, newName)
  1279  
  1280  	if err != nil {
  1281  		return err
  1282  	}
  1283  
  1284  	return db.SetRoot(ctx, newRoot)
  1285  }
  1286  
  1287  // GetViewDefinition implements sql.ViewDatabase
  1288  func (db Database) GetViewDefinition(ctx *sql.Context, viewName string) (sql.ViewDefinition, bool, error) {
  1289  	root, err := db.GetRoot(ctx)
  1290  	if err != nil {
  1291  		return sql.ViewDefinition{}, false, err
  1292  	}
  1293  
  1294  	lwrViewName := strings.ToLower(viewName)
  1295  	switch {
  1296  	case strings.HasPrefix(lwrViewName, doltdb.DoltBlameViewPrefix):
  1297  		tableName := lwrViewName[len(doltdb.DoltBlameViewPrefix):]
  1298  
  1299  		blameViewTextDef, err := dtables.NewBlameView(ctx, tableName, root)
  1300  		if err != nil {
  1301  			return sql.ViewDefinition{}, false, err
  1302  		}
  1303  		return sql.ViewDefinition{Name: viewName, TextDefinition: blameViewTextDef, CreateViewStatement: fmt.Sprintf("CREATE VIEW `%s` AS %s", viewName, blameViewTextDef)}, true, nil
  1304  	}
  1305  
  1306  	key, err := doltdb.NewDataCacheKey(root)
  1307  	if err != nil {
  1308  		return sql.ViewDefinition{}, false, err
  1309  	}
  1310  
  1311  	ds := dsess.DSessFromSess(ctx.Session)
  1312  	dbState, _, err := ds.LookupDbState(ctx, db.RevisionQualifiedName())
  1313  	if err != nil {
  1314  		return sql.ViewDefinition{}, false, err
  1315  	}
  1316  
  1317  	if dbState.SessionCache().ViewsCached(key) {
  1318  		view, ok := dbState.SessionCache().GetCachedViewDefinition(key, dsess.TableCacheKey{Name: viewName, Schema: db.schemaName})
  1319  		return view, ok, nil
  1320  	}
  1321  
  1322  	// TODO: do we need a dolt schemas table in every schema?
  1323  	tbl, ok, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName)
  1324  	if err != nil {
  1325  		return sql.ViewDefinition{}, false, err
  1326  	}
  1327  	if !ok {
  1328  		dbState.SessionCache().CacheViews(key, nil, db.schemaName)
  1329  		return sql.ViewDefinition{}, false, nil
  1330  	}
  1331  
  1332  	views, viewDef, found, err := getViewDefinitionFromSchemaFragmentsOfView(ctx, tbl.(*WritableDoltTable), viewName)
  1333  	if err != nil {
  1334  		return sql.ViewDefinition{}, false, err
  1335  	}
  1336  
  1337  	// TODO: only cache views from a single schema here
  1338  	dbState.SessionCache().CacheViews(key, views, db.schemaName)
  1339  
  1340  	return viewDef, found, nil
  1341  }
  1342  
  1343  func getViewDefinitionFromSchemaFragmentsOfView(ctx *sql.Context, tbl *WritableDoltTable, viewName string) ([]sql.ViewDefinition, sql.ViewDefinition, bool, error) {
  1344  	fragments, err := getSchemaFragmentsOfType(ctx, tbl, viewFragment)
  1345  	if err != nil {
  1346  		return nil, sql.ViewDefinition{}, false, err
  1347  	}
  1348  
  1349  	var found = false
  1350  	var viewDef sql.ViewDefinition
  1351  	var views = make([]sql.ViewDefinition, len(fragments))
  1352  	for i, fragment := range fragments {
  1353  		if strings.HasPrefix(strings.ToLower(fragments[i].fragment), "select") {
  1354  			// older versions
  1355  			views[i] = sql.ViewDefinition{
  1356  				Name:                fragments[i].name,
  1357  				TextDefinition:      fragments[i].fragment,
  1358  				CreateViewStatement: fmt.Sprintf("CREATE VIEW %s AS %s", fragments[i].name, fragments[i].fragment),
  1359  			}
  1360  		} else {
  1361  			views[i] = sql.ViewDefinition{
  1362  				Name: fragments[i].name,
  1363  				// TODO: need to define TextDefinition
  1364  				CreateViewStatement: fragments[i].fragment,
  1365  				SqlMode:             fragment.sqlMode,
  1366  			}
  1367  		}
  1368  
  1369  		if strings.ToLower(fragment.name) == strings.ToLower(viewName) {
  1370  			found = true
  1371  			viewDef = views[i]
  1372  		}
  1373  	}
  1374  
  1375  	return views, viewDef, found, nil
  1376  }
  1377  
  1378  // AllViews implements sql.ViewDatabase
  1379  func (db Database) AllViews(ctx *sql.Context) ([]sql.ViewDefinition, error) {
  1380  	tbl, ok, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName)
  1381  	if err != nil {
  1382  		return nil, err
  1383  	}
  1384  	if !ok {
  1385  		return nil, nil
  1386  	}
  1387  
  1388  	views, _, _, err := getViewDefinitionFromSchemaFragmentsOfView(ctx, tbl.(*WritableDoltTable), "")
  1389  	if err != nil {
  1390  		return nil, err
  1391  	}
  1392  
  1393  	return views, nil
  1394  }
  1395  
  1396  // CreateView implements sql.ViewCreator. Persists the view in the dolt database, so
  1397  // it can exist in a sql session later. Returns sql.ErrExistingView if a view
  1398  // with that name already exists.
  1399  func (db Database) CreateView(ctx *sql.Context, name string, selectStatement, createViewStmt string) error {
  1400  	err := sql.ErrExistingView.New(db.Name(), name)
  1401  	return db.addFragToSchemasTable(ctx, "view", name, createViewStmt, time.Unix(0, 0).UTC(), err)
  1402  }
  1403  
  1404  // DropView implements sql.ViewDropper. Removes a view from persistence in the
  1405  // dolt database. Returns sql.ErrNonExistingView if the view did not
  1406  // exist.
  1407  func (db Database) DropView(ctx *sql.Context, name string) error {
  1408  	err := sql.ErrViewDoesNotExist.New(db.baseName, name)
  1409  	return db.dropFragFromSchemasTable(ctx, "view", name, err)
  1410  }
  1411  
  1412  // GetTriggers implements sql.TriggerDatabase.
  1413  func (db Database) GetTriggers(ctx *sql.Context) ([]sql.TriggerDefinition, error) {
  1414  	root, err := db.GetRoot(ctx)
  1415  	if err != nil {
  1416  		return nil, nil
  1417  	}
  1418  
  1419  	key, err := doltdb.NewDataCacheKey(root)
  1420  	if err != nil {
  1421  		return nil, nil
  1422  	}
  1423  
  1424  	ds := dsess.DSessFromSess(ctx.Session)
  1425  	dbState, _, err := ds.LookupDbState(ctx, db.RevisionQualifiedName())
  1426  	if err != nil {
  1427  		return nil, nil
  1428  	}
  1429  
  1430  	var triggers []sql.TriggerDefinition
  1431  	var ok bool
  1432  	if triggers, ok = dbState.SessionCache().GetCachedTriggers(key, db.schemaName); ok {
  1433  		return triggers, nil
  1434  	}
  1435  
  1436  	defer func() {
  1437  		dbState.SessionCache().CacheTriggers(key, triggers, db.schemaName)
  1438  	}()
  1439  
  1440  	tbl, ok, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName)
  1441  	if err != nil {
  1442  		return nil, err
  1443  	}
  1444  	if !ok {
  1445  		return nil, nil
  1446  	}
  1447  
  1448  	frags, err := getSchemaFragmentsOfType(ctx, tbl.(*WritableDoltTable), triggerFragment)
  1449  	if err != nil {
  1450  		return nil, err
  1451  	}
  1452  
  1453  	for _, frag := range frags {
  1454  		triggers = append(triggers, sql.TriggerDefinition{
  1455  			Name:            frag.name,
  1456  			CreateStatement: frag.fragment,
  1457  			CreatedAt:       frag.created,
  1458  			SqlMode:         frag.sqlMode,
  1459  		})
  1460  	}
  1461  	if err != nil {
  1462  		return nil, err
  1463  	}
  1464  
  1465  	return triggers, nil
  1466  }
  1467  
  1468  // CreateTrigger implements sql.TriggerDatabase.
  1469  func (db Database) CreateTrigger(ctx *sql.Context, definition sql.TriggerDefinition) error {
  1470  	return db.addFragToSchemasTable(ctx,
  1471  		"trigger",
  1472  		definition.Name,
  1473  		definition.CreateStatement,
  1474  		definition.CreatedAt,
  1475  		fmt.Errorf("triggers `%s` already exists", definition.Name), //TODO: add a sql error and return that instead
  1476  	)
  1477  }
  1478  
  1479  // DropTrigger implements sql.TriggerDatabase.
  1480  func (db Database) DropTrigger(ctx *sql.Context, name string) error {
  1481  	//TODO: add a sql error and use that as the param error instead
  1482  	return db.dropFragFromSchemasTable(ctx, "trigger", name, sql.ErrTriggerDoesNotExist.New(name))
  1483  }
  1484  
  1485  // GetEvent implements sql.EventDatabase.
  1486  func (db Database) GetEvent(ctx *sql.Context, name string) (sql.EventDefinition, bool, error) {
  1487  	tbl, ok, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName)
  1488  	if err != nil {
  1489  		return sql.EventDefinition{}, false, err
  1490  	}
  1491  	if !ok {
  1492  		return sql.EventDefinition{}, false, nil
  1493  	}
  1494  
  1495  	frags, err := getSchemaFragmentsOfType(ctx, tbl.(*WritableDoltTable), eventFragment)
  1496  	if err != nil {
  1497  		return sql.EventDefinition{}, false, err
  1498  	}
  1499  
  1500  	for _, frag := range frags {
  1501  		if strings.ToLower(frag.name) == strings.ToLower(name) {
  1502  			event, err := db.createEventDefinitionFromFragment(ctx, frag)
  1503  			if err != nil {
  1504  				return sql.EventDefinition{}, false, err
  1505  			}
  1506  			return *event, true, nil
  1507  		}
  1508  	}
  1509  	return sql.EventDefinition{}, false, nil
  1510  }
  1511  
  1512  // GetEvents implements sql.EventDatabase.
  1513  func (db Database) GetEvents(ctx *sql.Context) (events []sql.EventDefinition, token interface{}, err error) {
  1514  	tbl, ok, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName)
  1515  	if err != nil {
  1516  		return nil, nil, err
  1517  	}
  1518  	if !ok {
  1519  		// If the dolt_schemas table doesn't exist, it's not an error, just no events
  1520  		return nil, nil, nil
  1521  	}
  1522  
  1523  	frags, err := getSchemaFragmentsOfType(ctx, tbl.(*WritableDoltTable), eventFragment)
  1524  	if err != nil {
  1525  		return nil, nil, err
  1526  	}
  1527  
  1528  	for _, frag := range frags {
  1529  		event, err := db.createEventDefinitionFromFragment(ctx, frag)
  1530  		if err != nil {
  1531  			return nil, nil, err
  1532  		}
  1533  		events = append(events, *event)
  1534  	}
  1535  
  1536  	// Grab a hash of the dolt_schemas table to use as the identifying token
  1537  	// to track if events need to be reloaded.
  1538  	tableHash, err := db.doltSchemaTableHash(ctx)
  1539  	if err != nil {
  1540  		return nil, nil, err
  1541  	}
  1542  
  1543  	return events, tableHash, nil
  1544  }
  1545  
  1546  // NeedsToReloadEvents implements sql.EventDatabase.
  1547  func (db Database) NeedsToReloadEvents(ctx *sql.Context, token interface{}) (bool, error) {
  1548  	// A nil token means no events in this db. If the dolt_schemas table doesn't exist, it will have a zero hash below
  1549  	// as well, meaning we don't reload events in that case.
  1550  	if token == nil {
  1551  		token = hash.Hash{}
  1552  	}
  1553  
  1554  	hash, ok := token.(hash.Hash)
  1555  	if !ok {
  1556  		return false, fmt.Errorf("expected token to be hash.Hash, but received %T", token)
  1557  	}
  1558  
  1559  	tableHash, err := db.doltSchemaTableHash(ctx)
  1560  	if err != nil {
  1561  		return false, err
  1562  	}
  1563  
  1564  	// If the current hash doesn't match what we last loaded, then we
  1565  	// need to reload event definitions
  1566  	return !tableHash.Equal(hash), nil
  1567  }
  1568  
  1569  // doltSchemaTableHash returns the hash of the dolt_schemas table, or any error encountered along the way.
  1570  func (db Database) doltSchemaTableHash(ctx *sql.Context) (hash.Hash, error) {
  1571  	root, err := db.GetRoot(ctx)
  1572  	if err != nil {
  1573  		return hash.Hash{}, err
  1574  	}
  1575  
  1576  	tableHash, _, err := root.GetTableHash(ctx, doltdb.SchemasTableName)
  1577  	return tableHash, err
  1578  }
  1579  
  1580  // createEventDefinitionFromFragment creates an EventDefinition instance from the schema fragment |frag|.
  1581  func (db Database) createEventDefinitionFromFragment(ctx *sql.Context, frag schemaFragment) (*sql.EventDefinition, error) {
  1582  	b := planbuilder.New(ctx, db.getCatalog(ctx), sql.NewMysqlParser())
  1583  	b.SetParserOptions(sql.NewSqlModeFromString(frag.sqlMode).ParserOptions())
  1584  	parsed, _, _, err := b.Parse(updateEventStatusTemporarilyForNonDefaultBranch(db.revision, frag.fragment), false)
  1585  	if err != nil {
  1586  		return nil, err
  1587  	}
  1588  
  1589  	eventPlan, ok := parsed.(*plan.CreateEvent)
  1590  	if !ok {
  1591  		return nil, fmt.Errorf("unexpected type %T for create event statement", eventPlan)
  1592  	}
  1593  
  1594  	// NOTE: Time fields for events are assumed to be specified in the session's timezone, which defaults to the
  1595  	//       system's timezone. When we store them, we store them at UTC, and when we send them back to a caller
  1596  	//       we convert them back to the caller's session timezone.
  1597  	//       Here we are loading the events from disk, so they are already in UTC and don't need any other
  1598  	//       timezone applied, so we specify "+00:00".
  1599  	event, err := eventPlan.GetEventDefinition(ctx, frag.created, frag.created, frag.created, "+00:00")
  1600  	if err != nil {
  1601  		return nil, err
  1602  	}
  1603  	event.SqlMode = frag.sqlMode
  1604  
  1605  	return &event, nil
  1606  }
  1607  
  1608  // getCatalog creates and returns the analyzer.Catalog instance for this database.
  1609  func (db Database) getCatalog(ctx *sql.Context) *analyzer.Catalog {
  1610  	doltSession := dsess.DSessFromSess(ctx.Session)
  1611  	return sqle.NewDefault(doltSession.Provider()).Analyzer.Catalog
  1612  }
  1613  
  1614  // SaveEvent implements sql.EventDatabase.
  1615  func (db Database) SaveEvent(ctx *sql.Context, event sql.EventDefinition) (bool, error) {
  1616  	// If the database is not the default branch database, then the event is disabled.
  1617  	// TODO: need better way to determine the default branch; currently it checks only 'main'
  1618  	if db.revision != env.DefaultInitBranch && event.Status == sql.EventStatus_Enable.String() {
  1619  		// using revision database name
  1620  		event.Status = sql.EventStatus_Disable.String()
  1621  		ctx.Session.Warn(&sql.Warning{
  1622  			Level:   "Warning",
  1623  			Code:    1105,
  1624  			Message: fmt.Sprintf("Event status cannot be enabled for revision database."),
  1625  		})
  1626  	}
  1627  
  1628  	// TODO: store LastAltered, LastExecuted and TimezoneOffset in appropriate place
  1629  	return event.Status == sql.EventStatus_Enable.String(), db.addFragToSchemasTable(ctx,
  1630  		eventFragment,
  1631  		event.Name,
  1632  		event.CreateEventStatement(),
  1633  		event.CreatedAt,
  1634  		sql.ErrEventAlreadyExists.New(event.Name),
  1635  	)
  1636  }
  1637  
  1638  // DropEvent implements sql.EventDatabase.
  1639  func (db Database) DropEvent(ctx *sql.Context, name string) error {
  1640  	return db.dropFragFromSchemasTable(ctx, eventFragment, name, sql.ErrEventDoesNotExist.New(name))
  1641  }
  1642  
  1643  // UpdateEvent implements sql.EventDatabase.
  1644  func (db Database) UpdateEvent(ctx *sql.Context, originalName string, event sql.EventDefinition) (bool, error) {
  1645  	// TODO: any EVENT STATUS change should also update the branch-specific event scheduling
  1646  	err := db.DropEvent(ctx, originalName)
  1647  	if err != nil {
  1648  		return false, err
  1649  	}
  1650  	return db.SaveEvent(ctx, event)
  1651  }
  1652  
  1653  // UpdateLastExecuted implements sql.EventDatabase
  1654  func (db Database) UpdateLastExecuted(ctx *sql.Context, eventName string, lastExecuted time.Time) error {
  1655  	// TODO: update LastExecuted in appropriate place
  1656  	return nil
  1657  }
  1658  
  1659  // updateEventStatusTemporarilyForNonDefaultBranch updates the event status from ENABLE to DISABLE if it's not default branch.
  1660  // The event status metadata is not updated in storage, but only for display purposes we return event status as 'DISABLE'.
  1661  // This function is used temporarily to implement logic of only allowing enabled events to be executed on default branch.
  1662  func updateEventStatusTemporarilyForNonDefaultBranch(revision, createStmt string) string {
  1663  	// TODO: need better way to determine the default branch; currently it checks only 'main'
  1664  
  1665  	if revision == "" || revision == env.DefaultInitBranch {
  1666  		return createStmt
  1667  	}
  1668  	return strings.Replace(createStmt, "ENABLE", "DISABLE", 1)
  1669  }
  1670  
  1671  // GetStoredProcedure implements sql.StoredProcedureDatabase.
  1672  func (db Database) GetStoredProcedure(ctx *sql.Context, name string) (sql.StoredProcedureDetails, bool, error) {
  1673  	procedures, err := DoltProceduresGetAll(ctx, db, strings.ToLower(name))
  1674  	if err != nil {
  1675  		return sql.StoredProcedureDetails{}, false, nil
  1676  	}
  1677  	if len(procedures) == 1 {
  1678  		return procedures[0], true, nil
  1679  	}
  1680  	return sql.StoredProcedureDetails{}, false, nil
  1681  }
  1682  
  1683  // GetStoredProcedures implements sql.StoredProcedureDatabase.
  1684  func (db Database) GetStoredProcedures(ctx *sql.Context) ([]sql.StoredProcedureDetails, error) {
  1685  	return DoltProceduresGetAll(ctx, db, "")
  1686  }
  1687  
  1688  // SaveStoredProcedure implements sql.StoredProcedureDatabase.
  1689  func (db Database) SaveStoredProcedure(ctx *sql.Context, spd sql.StoredProcedureDetails) error {
  1690  	if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Write); err != nil {
  1691  		return err
  1692  	}
  1693  	return DoltProceduresAddProcedure(ctx, db, spd)
  1694  }
  1695  
  1696  // DropStoredProcedure implements sql.StoredProcedureDatabase.
  1697  func (db Database) DropStoredProcedure(ctx *sql.Context, name string) error {
  1698  	if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Write); err != nil {
  1699  		return err
  1700  	}
  1701  	return DoltProceduresDropProcedure(ctx, db, name)
  1702  }
  1703  
  1704  func (db Database) addFragToSchemasTable(ctx *sql.Context, fragType, name, definition string, created time.Time, existingErr error) (err error) {
  1705  	if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Write); err != nil {
  1706  		return err
  1707  	}
  1708  	tbl, err := getOrCreateDoltSchemasTable(ctx, db)
  1709  	if err != nil {
  1710  		return err
  1711  	}
  1712  
  1713  	_, exists, err := fragFromSchemasTable(ctx, tbl, fragType, name)
  1714  	if err != nil {
  1715  		return err
  1716  	}
  1717  	if exists {
  1718  		return existingErr
  1719  	}
  1720  
  1721  	// Insert the new row into the db
  1722  	inserter := tbl.Inserter(ctx)
  1723  	defer func() {
  1724  		cErr := inserter.Close(ctx)
  1725  		if err == nil {
  1726  			err = cErr
  1727  		}
  1728  	}()
  1729  	// Encode createdAt time to JSON
  1730  	extra := Extra{
  1731  		CreatedAt: created.Unix(),
  1732  	}
  1733  	extraJSON, err := json.Marshal(extra)
  1734  	if err != nil {
  1735  		return err
  1736  	}
  1737  
  1738  	sqlMode := sql.LoadSqlMode(ctx)
  1739  
  1740  	return inserter.Insert(ctx, sql.Row{fragType, name, definition, extraJSON, sqlMode.String()})
  1741  }
  1742  
  1743  func (db Database) dropFragFromSchemasTable(ctx *sql.Context, fragType, name string, missingErr error) error {
  1744  	if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Write); err != nil {
  1745  		return err
  1746  	}
  1747  
  1748  	stbl, found, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName)
  1749  	if err != nil {
  1750  		return err
  1751  	}
  1752  	if !found {
  1753  		return missingErr
  1754  	}
  1755  
  1756  	tbl := stbl.(*WritableDoltTable)
  1757  	row, exists, err := fragFromSchemasTable(ctx, tbl, fragType, name)
  1758  	if err != nil {
  1759  		return err
  1760  	}
  1761  	if !exists {
  1762  		return missingErr
  1763  	}
  1764  	deleter := tbl.Deleter(ctx)
  1765  	err = deleter.Delete(ctx, row)
  1766  	if err != nil {
  1767  		return err
  1768  	}
  1769  
  1770  	err = deleter.Close(ctx)
  1771  	if err != nil {
  1772  		return err
  1773  	}
  1774  
  1775  	// If the dolt schemas table is now empty, drop it entirely. This is necessary to prevent the creation and
  1776  	// immediate dropping of views or triggers, when none previously existed, from changing the database state.
  1777  	return db.dropTableIfEmpty(ctx, doltdb.SchemasTableName)
  1778  }
  1779  
  1780  // dropTableIfEmpty drops the table named if it exists and has at least one row.
  1781  func (db Database) dropTableIfEmpty(ctx *sql.Context, tableName string) error {
  1782  	stbl, found, err := db.GetTableInsensitive(ctx, tableName)
  1783  	if err != nil {
  1784  		return err
  1785  	}
  1786  	if !found {
  1787  		return nil
  1788  	}
  1789  
  1790  	table, err := stbl.(*WritableDoltTable).DoltTable.DoltTable(ctx)
  1791  	if err != nil {
  1792  		return err
  1793  	}
  1794  
  1795  	rows, err := table.GetRowData(ctx)
  1796  	if err != nil {
  1797  		return err
  1798  	}
  1799  
  1800  	numRows, err := rows.Count()
  1801  	if err != nil {
  1802  		return err
  1803  	}
  1804  
  1805  	if numRows == 0 {
  1806  		return db.dropTable(ctx, tableName)
  1807  	}
  1808  
  1809  	return nil
  1810  }
  1811  
  1812  // GetAllTemporaryTables returns all temporary tables
  1813  func (db Database) GetAllTemporaryTables(ctx *sql.Context) ([]sql.Table, error) {
  1814  	sess := dsess.DSessFromSess(ctx.Session)
  1815  	return sess.GetAllTemporaryTables(ctx, db.Name())
  1816  }
  1817  
  1818  // GetCollation implements the interface sql.CollatedDatabase.
  1819  func (db Database) GetCollation(ctx *sql.Context) sql.CollationID {
  1820  	root, err := db.GetRoot(ctx)
  1821  	if err != nil {
  1822  		return sql.Collation_Default
  1823  	}
  1824  	collation, err := root.GetCollation(ctx)
  1825  	if err != nil {
  1826  		return sql.Collation_Default
  1827  	}
  1828  	return sql.CollationID(collation)
  1829  }
  1830  
  1831  // SetCollation implements the interface sql.CollatedDatabase.
  1832  func (db Database) SetCollation(ctx *sql.Context, collation sql.CollationID) error {
  1833  	if err := dsess.CheckAccessForDb(ctx, db, branch_control.Permissions_Write); err != nil {
  1834  		return err
  1835  	}
  1836  	if collation == sql.Collation_Unspecified {
  1837  		collation = sql.Collation_Default
  1838  	}
  1839  	root, err := db.GetRoot(ctx)
  1840  	if err != nil {
  1841  		return err
  1842  	}
  1843  	newRoot, err := root.SetCollation(ctx, schema.Collation(collation))
  1844  	if err != nil {
  1845  		return err
  1846  	}
  1847  	return db.SetRoot(ctx, newRoot)
  1848  }
  1849  
  1850  // LoadRebasePlan implements the rebase.RebasePlanDatabase interface
  1851  func (db Database) LoadRebasePlan(ctx *sql.Context) (*rebase.RebasePlan, error) {
  1852  	table, ok, err := db.GetTableInsensitive(ctx, doltdb.RebaseTableName)
  1853  	if err != nil {
  1854  		return nil, err
  1855  	}
  1856  	if !ok {
  1857  		return nil, fmt.Errorf("unable to find dolt_rebase table")
  1858  	}
  1859  	resolvedTable := plan.NewResolvedTable(table, db, nil)
  1860  	sort := plan.NewSort([]sql.SortField{{
  1861  		Column: expression.NewGetField(0, types.MustCreateDecimalType(6, 2), "rebase_order", false),
  1862  		Order:  sql.Ascending,
  1863  	}}, resolvedTable)
  1864  	iter, err := rowexec.DefaultBuilder.Build(ctx, sort, nil)
  1865  	if err != nil {
  1866  		return nil, err
  1867  	}
  1868  
  1869  	var rebasePlan rebase.RebasePlan
  1870  	for {
  1871  		row, err := iter.Next(ctx)
  1872  		if err == io.EOF {
  1873  			break
  1874  		} else if err != nil {
  1875  			return nil, err
  1876  		}
  1877  
  1878  		i, ok := row[1].(uint16)
  1879  		if !ok {
  1880  			return nil, fmt.Errorf("invalid enum value in rebase plan: %v (%T)", row[1], row[1])
  1881  		}
  1882  		rebaseAction, ok := dprocedures.RebaseActionEnumType.At(int(i))
  1883  		if !ok {
  1884  			return nil, fmt.Errorf("invalid enum value in rebase plan: %v (%T)", row[1], row[1])
  1885  		}
  1886  
  1887  		rebasePlan.Steps = append(rebasePlan.Steps, rebase.RebasePlanStep{
  1888  			RebaseOrder: row[0].(decimal.Decimal),
  1889  			Action:      rebaseAction,
  1890  			CommitHash:  row[2].(string),
  1891  			CommitMsg:   row[3].(string),
  1892  		})
  1893  	}
  1894  
  1895  	return &rebasePlan, nil
  1896  }
  1897  
  1898  // SaveRebasePlan implements the rebase.RebasePlanDatabase interface
  1899  func (db Database) SaveRebasePlan(ctx *sql.Context, plan *rebase.RebasePlan) error {
  1900  	pkSchema := sql.NewPrimaryKeySchema(dprocedures.DoltRebaseSystemTableSchema)
  1901  	// we use createSqlTable, instead of CreateTable to avoid the "dolt_" reserved prefix table name check
  1902  	err := db.createSqlTable(ctx, doltdb.RebaseTableName, "", pkSchema, sql.Collation_Default, "")
  1903  	if err != nil {
  1904  		return err
  1905  	}
  1906  
  1907  	table, ok, err := db.GetTableInsensitive(ctx, doltdb.RebaseTableName)
  1908  	if err != nil {
  1909  		return err
  1910  	}
  1911  	if !ok {
  1912  		return fmt.Errorf("unable to find %s table", doltdb.RebaseTableName)
  1913  	}
  1914  
  1915  	writeableDoltTable, ok := table.(*WritableDoltTable)
  1916  	if !ok {
  1917  		return fmt.Errorf("expected a *sqle.WritableDoltTable, but got %T", table)
  1918  	}
  1919  
  1920  	inserter := writeableDoltTable.Inserter(ctx)
  1921  	for _, planMember := range plan.Steps {
  1922  		actionEnumValue := dprocedures.RebaseActionEnumType.IndexOf(strings.ToLower(planMember.Action))
  1923  		if actionEnumValue == -1 {
  1924  			return fmt.Errorf("invalid rebase action: %s", planMember.Action)
  1925  		}
  1926  		err = inserter.Insert(ctx, sql.Row{
  1927  			planMember.RebaseOrder,
  1928  			uint16(actionEnumValue),
  1929  			planMember.CommitHash,
  1930  			planMember.CommitMsg,
  1931  		})
  1932  		if err != nil {
  1933  			return err
  1934  		}
  1935  	}
  1936  
  1937  	return inserter.Close(ctx)
  1938  }
  1939  
  1940  // noopRepoStateWriter is a minimal implementation of RepoStateWriter that does nothing
  1941  type noopRepoStateWriter struct{}
  1942  
  1943  func (n noopRepoStateWriter) UpdateStagedRoot(ctx context.Context, newRoot doltdb.RootValue) error {
  1944  	return nil
  1945  }
  1946  
  1947  func (n noopRepoStateWriter) UpdateWorkingRoot(ctx context.Context, newRoot doltdb.RootValue) error {
  1948  	return nil
  1949  }
  1950  
  1951  func (n noopRepoStateWriter) SetCWBHeadRef(ctx context.Context, marshalableRef ref.MarshalableRef) error {
  1952  	return nil
  1953  }
  1954  
  1955  func (n noopRepoStateWriter) AddRemote(r env.Remote) error {
  1956  	return nil
  1957  }
  1958  
  1959  func (n noopRepoStateWriter) AddBackup(r env.Remote) error {
  1960  	return nil
  1961  }
  1962  
  1963  func (n noopRepoStateWriter) RemoveRemote(ctx context.Context, name string) error {
  1964  	return nil
  1965  }
  1966  
  1967  func (n noopRepoStateWriter) RemoveBackup(ctx context.Context, name string) error {
  1968  	return nil
  1969  }
  1970  
  1971  func (n noopRepoStateWriter) TempTableFilesDir() (string, error) {
  1972  	return "", nil
  1973  }
  1974  
  1975  func (n noopRepoStateWriter) UpdateBranch(name string, new env.BranchConfig) error {
  1976  	return nil
  1977  }