Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for schemas in various version control operations #8343

Merged
merged 26 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5913ab3
Fixed bug in moving a table between roots when the schema it's in onl…
zachmu Sep 5, 2024
645a479
Added schema deltas to dolt_status
zachmu Sep 6, 2024
17de502
Plumbing table names to places required
zachmu Sep 6, 2024
7a82981
More plumbing of table name, backed out a couple places it wasn't act…
zachmu Sep 6, 2024
fd6f882
More plumbing
zachmu Sep 6, 2024
8da0101
More plumbing
zachmu Sep 6, 2024
862da2d
Getting very deep
zachmu Sep 6, 2024
ed433ec
Stable version for doltgres tests
zachmu Sep 6, 2024
e50dc2f
Type fix
zachmu Sep 6, 2024
03b5434
Removed debug printing, fixed tests
zachmu Sep 7, 2024
99af67d
Bug fix for merging a new schema
zachmu Sep 7, 2024
0125ad6
Stage schemas with no tables in them
zachmu Sep 9, 2024
a7dc990
Experiment: create a new commit after creating a database
zachmu Sep 10, 2024
f9d553a
Tests for create database
zachmu Sep 10, 2024
a218362
New failing test
zachmu Sep 10, 2024
ec621a6
Add an additional commit for CREATE DATABASE in some cases
zachmu Sep 10, 2024
b5e88ad
Fully working transaction management around create database
zachmu Sep 10, 2024
635952e
Bug fix for schema name deltas
zachmu Sep 11, 2024
f97495a
Fixed tests
zachmu Sep 11, 2024
aa887c8
Bug fix
zachmu Sep 11, 2024
a9e1dd6
More test fixes
zachmu Sep 11, 2024
b25f3ef
Amended note for why amend won't work
zachmu Sep 11, 2024
db831c5
Missing copyright headers
zachmu Sep 11, 2024
9d6c7ae
[ga-format-pr] Run go/utils/repofmt/format_repo.sh and go/Godeps/upda…
zachmu Sep 11, 2024
2412d09
removed stale comment
zachmu Sep 11, 2024
0d463ca
Merge branch 'zachmu/schema-commit' of github.com:dolthub/dolt into z…
zachmu Sep 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions go/cmd/dolt/commands/cvcmds/verify_constraints.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/merge"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
"github.com/dolthub/dolt/go/libraries/utils/set"
"github.com/dolthub/dolt/go/store/types"
)

Expand Down Expand Up @@ -85,7 +84,12 @@ func (cmd VerifyConstraintsCmd) Exec(ctx context.Context, commandStr string, arg
return commands.HandleVErrAndExitCode(errhand.BuildDError("Unable to read table names.").AddCause(err).Build(), nil)
}
}
tableSet := set.NewStrSet(tableNames)
tableSet := doltdb.NewTableNameSet(nil)

// TODO: schema names
for _, tableName := range tableNames {
tableSet.Add(doltdb.TableName{Name: tableName})
}

comparingRoot, err := dEnv.HeadRoot(ctx)
if err != nil {
Expand Down
106 changes: 106 additions & 0 deletions go/libraries/doltcore/diff/database_schema_deltas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2024 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package diff

import (
"context"

"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/utils/set"
)

// DatabaseSchemaDelta represents a change in the set of database schemas between two roots
type DatabaseSchemaDelta struct {
FromName string
ToName string
}

func (d DatabaseSchemaDelta) IsAdd() bool {
return d.FromName == "" && d.ToName != ""
}

func (d DatabaseSchemaDelta) IsDrop() bool {
return d.FromName != "" && d.ToName == ""
}

func (d DatabaseSchemaDelta) CurName() string {
if d.ToName != "" {
return d.ToName
}
return d.FromName
}

// GetDatabaseSchemaDeltas returns a list of DatabaseSchemaDelta objects representing the changes in database schemas
func GetDatabaseSchemaDeltas(ctx context.Context, fromRoot, toRoot doltdb.RootValue) ([]DatabaseSchemaDelta, error) {
fromNames, err := getDatabaseSchemaNames(ctx, fromRoot)
if err != nil {
return nil, err
}

toNames, err := getDatabaseSchemaNames(ctx, toRoot)
if err != nil {
return nil, err
}

// short circuit for common case where there are no schemas (dolt)
if fromNames.Size() == 0 && toNames.Size() == 0 {
return nil, nil
}

// generate a diff for each schema name that's present in one root but not the other
var deltas []DatabaseSchemaDelta
fromNames.Iterate(func(name string) (cont bool) {
if !toNames.Contains(name) {
deltas = append(deltas, DatabaseSchemaDelta{FromName: name})
}
return true
})

toNames.Iterate(func(name string) (cont bool) {
if !fromNames.Contains(name) {
deltas = append(deltas, DatabaseSchemaDelta{ToName: name})
}
return true
})

return deltas, nil
}

// GetStagedUnstagedDatabaseSchemaDeltas represents staged and unstaged changes as DatabaseSchemaDelta slices.
func GetStagedUnstagedDatabaseSchemaDeltas(ctx context.Context, roots doltdb.Roots) (staged, unstaged []DatabaseSchemaDelta, err error) {
staged, err = GetDatabaseSchemaDeltas(ctx, roots.Head, roots.Staged)
if err != nil {
return nil, nil, err
}

unstaged, err = GetDatabaseSchemaDeltas(ctx, roots.Staged, roots.Working)
if err != nil {
return nil, nil, err
}

return staged, unstaged, nil
}

func getDatabaseSchemaNames(ctx context.Context, root doltdb.RootValue) (*set.StrSet, error) {
dbSchemaNames := set.NewEmptyStrSet()
dbSchemas, err := root.GetDatabaseSchemas(ctx)
if err != nil {
return nil, err
}
for _, dbSchema := range dbSchemas {
dbSchemaNames.Add(dbSchema.Name)
}
return dbSchemaNames, nil
}
8 changes: 5 additions & 3 deletions go/libraries/doltcore/doltdb/foreign_key_coll.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ OuterLoop:
// and any keys in the collection are unresolved. A "dirty resolution" is performed, which matches the column names to
// tags, and then a standard tag comparison is performed. If a table or column is not in the map, then the foreign key
// is ignored.
func (fkc *ForeignKeyCollection) GetMatchingKey(fk ForeignKey, allSchemas map[string]schema.Schema, matchUnresolvedKeyToResolvedKey bool) (ForeignKey, bool) {
func (fkc *ForeignKeyCollection) GetMatchingKey(fk ForeignKey, allSchemas map[TableName]schema.Schema, matchUnresolvedKeyToResolvedKey bool) (ForeignKey, bool) {
if !fk.IsResolved() {
// The given foreign key is unresolved, so we only look for matches on unresolved keys
OuterLoopUnresolved:
Expand Down Expand Up @@ -543,11 +543,13 @@ OuterLoopResolved:
len(fk.ReferencedTableColumns) != len(existingFk.UnresolvedFKDetails.ReferencedTableColumns) {
continue
}
tblSch, ok := allSchemas[existingFk.TableName]
// TODO: schema name
tblSch, ok := allSchemas[TableName{Name: existingFk.TableName}]
if !ok {
continue
}
refTblSch, ok := allSchemas[existingFk.ReferencedTableName]
// TODO: schema name
refTblSch, ok := allSchemas[TableName{Name: existingFk.ReferencedTableName}]
if !ok {
continue
}
Expand Down
7 changes: 3 additions & 4 deletions go/libraries/doltcore/doltdb/root_val.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,11 +455,10 @@ func GetExistingColumns(
return existingCols, nil
}

func GetAllSchemas(ctx context.Context, root RootValue) (map[string]schema.Schema, error) {
m := make(map[string]schema.Schema)
func GetAllSchemas(ctx context.Context, root RootValue) (map[TableName]schema.Schema, error) {
m := make(map[TableName]schema.Schema)
err := root.IterTables(ctx, func(name TableName, table *Table, sch schema.Schema) (stop bool, err error) {
// TODO: schema name
m[name.Name] = sch
m[name] = sch
return false, nil
})

Expand Down
8 changes: 4 additions & 4 deletions go/libraries/doltcore/doltdb/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ func (t *Table) clearConflicts(ctx context.Context) (*Table, error) {
}

// GetConflictSchemas returns the merge conflict schemas for this table.
func (t *Table) GetConflictSchemas(ctx context.Context, tblName string) (base, sch, mergeSch schema.Schema, err error) {
func (t *Table) GetConflictSchemas(ctx context.Context, tblName TableName) (base, sch, mergeSch schema.Schema, err error) {
if t.Format() == types.Format_DOLT {
return t.getProllyConflictSchemas(ctx, tblName)
}
Expand All @@ -267,7 +267,7 @@ func (t *Table) GetConflictSchemas(ctx context.Context, tblName string) (base, s
// The conflict schema is implicitly determined based on the first conflict in the artifacts table.
// For now, we will enforce that all conflicts in the artifacts table must have the same schema set (base, ours, theirs).
// In the future, we may be able to display conflicts in a way that allows different conflict schemas to coexist.
func (t *Table) getProllyConflictSchemas(ctx context.Context, tblName string) (base, sch, mergeSch schema.Schema, err error) {
func (t *Table) getProllyConflictSchemas(ctx context.Context, tblName TableName) (base, sch, mergeSch schema.Schema, err error) {
arts, err := t.GetArtifacts(ctx)
if err != nil {
return nil, nil, nil, err
Expand Down Expand Up @@ -331,12 +331,12 @@ func (t *Table) getProllyConflictSchemas(ctx context.Context, tblName string) (b
return baseSch, ourSch, theirSch, nil
}

func tableFromRootIsh(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, h hash.Hash, tblName string) (*Table, bool, error) {
func tableFromRootIsh(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, h hash.Hash, tblName TableName) (*Table, bool, error) {
rv, err := LoadRootValueFromRootIshAddr(ctx, vrw, ns, h)
if err != nil {
return nil, false, err
}
tbl, ok, err := rv.GetTable(ctx, TableName{Name: tblName})
tbl, ok, err := rv.GetTable(ctx, tblName)
if err != nil {
return nil, false, err
}
Expand Down
4 changes: 2 additions & 2 deletions go/libraries/doltcore/doltdb/workingset.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ type MergeState struct {
type SchemaConflict struct {
ToSch, FromSch schema.Schema
ToFks, FromFks []ForeignKey
ToParentSchemas map[string]schema.Schema
FromParentSchemas map[string]schema.Schema
ToParentSchemas map[TableName]schema.Schema
FromParentSchemas map[TableName]schema.Schema
toTbl, fromTbl *Table
}

Expand Down
11 changes: 8 additions & 3 deletions go/libraries/doltcore/env/actions/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,26 @@ func GetCommitStaged(
return nil, datas.ErrEmptyCommitMessage
}

staged, notStaged, err := diff.GetStagedUnstagedTableDeltas(ctx, roots)
stagedTables, notStaged, err := diff.GetStagedUnstagedTableDeltas(ctx, roots)
if err != nil {
return nil, err
}

var stagedTblNames []doltdb.TableName
for _, td := range staged {
for _, td := range stagedTables {
n := td.ToName
if td.IsDrop() {
n = td.FromName
}
stagedTblNames = append(stagedTblNames, n)
}

isEmpty := len(staged) == 0
stagedSchemas, _, err := diff.GetStagedUnstagedDatabaseSchemaDeltas(ctx, roots)
if err != nil {
return nil, err
}

isEmpty := len(stagedTables) == 0 && len(stagedSchemas) == 0
allowEmpty := ws.MergeActive() || props.AllowEmpty || props.Amend

if isEmpty && props.SkipEmpty {
Expand Down
11 changes: 5 additions & 6 deletions go/libraries/doltcore/env/actions/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/resolve"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
"github.com/dolthub/dolt/go/store/datas"
)
Expand Down Expand Up @@ -81,7 +80,7 @@ func resetHardTables(ctx *sql.Context, dbData env.DbData, cSpecStr string, roots
return nil, doltdb.Roots{}, err
}
for _, name := range staged {
delete(untracked, name)
delete(untracked, doltdb.TableName{Name: name})
}

newWkRoot := roots.Head
Expand All @@ -102,15 +101,15 @@ func resetHardTables(ctx *sql.Context, dbData env.DbData, cSpecStr string, roots
}

for name := range untracked {
tname, tbl, exists, err := resolve.Table(ctx, roots.Working, name)
tbl, exists, err := roots.Working.GetTable(ctx, name)
if err != nil {
return nil, doltdb.Roots{}, err
}
if !exists {
return nil, doltdb.Roots{}, fmt.Errorf("untracked table %s does not exist in working set", name)
}

newWkRoot, err = newWkRoot.PutTable(ctx, tname, tbl)
newWkRoot, err = newWkRoot.PutTable(ctx, name, tbl)
if err != nil {
return nil, doltdb.Roots{}, fmt.Errorf("failed to write table back to database: %s", err)
}
Expand Down Expand Up @@ -334,11 +333,11 @@ func CleanUntracked(ctx context.Context, roots doltdb.Roots, tables []string, dr

// mapColumnTags takes a map from table name to schema.Schema and generates
// a map from column tags to table names (see RootValue.GetAllSchemas).
func mapColumnTags(tables map[string]schema.Schema) (m map[uint64]string) {
func mapColumnTags(tables map[doltdb.TableName]schema.Schema) (m map[uint64]string) {
m = make(map[uint64]string, len(tables))
for tbl, sch := range tables {
for _, tag := range sch.GetAllCols().Tags {
m[tag] = tbl
m[tag] = tbl.Name
}
}
return
Expand Down
54 changes: 53 additions & 1 deletion go/libraries/doltcore/env/actions/staged.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/dolthub/dolt/go/libraries/doltcore/diff"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
)

func StageTables(ctx context.Context, roots doltdb.Roots, tbls []doltdb.TableName, filterIgnoredTables bool) (doltdb.Roots, error) {
Expand All @@ -45,7 +46,58 @@ func StageAllTables(ctx context.Context, roots doltdb.Roots, filterIgnoredTables
return doltdb.Roots{}, err
}

return StageTables(ctx, roots, tbls, filterIgnoredTables)
roots, err = StageTables(ctx, roots, tbls, filterIgnoredTables)
if err != nil {
return doltdb.Roots{}, err
}

roots, err = StageAllSchemas(ctx, roots)
if err != nil {
return doltdb.Roots{}, err
}

return roots, nil
}

func StageAllSchemas(ctx context.Context, roots doltdb.Roots) (doltdb.Roots, error) {
newStaged, err := MoveAllSchemasBetweenRoots(ctx, roots.Working, roots.Staged)
if err != nil {
return doltdb.Roots{}, err
}

roots.Staged = newStaged
return roots, nil
}

// MoveAllSchemasBetweenRoots copies all schemas from the src RootValue to the dest RootValue.
func MoveAllSchemasBetweenRoots(ctx context.Context, src, dest doltdb.RootValue) (doltdb.RootValue, error) {
srcSchemaNames, err := getDatabaseSchemaNames(ctx, src)
if err != nil {
return nil, err
}

if srcSchemaNames.Size() == 0 {
return dest, nil
}

destSchemaNames, err := getDatabaseSchemaNames(ctx, dest)
if err != nil {
return nil, err
}

srcSchemaNames.Iterate(func(schemaName string) (cont bool) {
if !destSchemaNames.Contains(schemaName) {
dest, err = dest.CreateDatabaseSchema(ctx, schema.DatabaseSchema{
Name: schemaName,
})
if err != nil {
return false
}
}
return true
})

return dest, nil
}

func StageDatabase(ctx context.Context, roots doltdb.Roots) (doltdb.Roots, error) {
Expand Down
Loading
Loading