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 }