Merge pull request #10481 from dolthub/zachmu/kill-noms2

[no-review-notes] Removed most of the remaining LD_1 specific codepaths and types
This commit is contained in:
Zach Musgrave
2026-02-11 14:58:55 -08:00
committed by GitHub
68 changed files with 277 additions and 30588 deletions
-117
View File
@@ -1,117 +0,0 @@
// Copyright 2019 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 commands
import (
"context"
"os"
eventsapi "github.com/dolthub/eventsapi_schema/dolt/services/eventsapi/v1alpha1"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/migrate"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
"github.com/dolthub/dolt/go/store/types"
)
const (
migrationPrompt = `Run "dolt migrate" to update this database to the latest data format`
migrationMsg = "Migrating database to the latest data format"
migrateDropConflictsFlag = "drop-conflicts"
)
var migrateDocs = cli.CommandDocumentationContent{
ShortDesc: "Executes a database migration to use the latest Dolt data format.",
LongDesc: `Migrate is a multi-purpose command to update the data format of a Dolt database. Over time, development
on Dolt requires changes to the on-disk data format. These changes are necessary to improve Database performance and
correctness. Migrating to the latest format is therefore necessary for compatibility with the latest Dolt clients, and
to take advantage of the newly released Dolt features.`,
Synopsis: []string{
"[ --push ] [ --pull ]",
},
}
type MigrateCmd struct{}
// Name is returns the name of the Dolt cli command. This is what is used on the command line to invoke the command
func (cmd MigrateCmd) Name() string {
return "migrate"
}
// Description returns a description of the command
func (cmd MigrateCmd) Description() string {
return migrateDocs.ShortDesc
}
func (cmd MigrateCmd) Docs() *cli.CommandDocumentation {
return nil
}
func (cmd MigrateCmd) ArgParser() *argparser.ArgParser {
ap := argparser.NewArgParserWithMaxArgs(cmd.Name(), 0)
ap.SupportsFlag(migrateDropConflictsFlag, "", "Drop any conflicts visited during the migration")
return ap
}
// EventType returns the type of the event to log
func (cmd MigrateCmd) EventType() eventsapi.ClientEventType {
return eventsapi.ClientEventType_MIGRATE
}
// Exec executes the command
func (cmd MigrateCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int {
ap := cmd.ArgParser()
help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, migrateDocs, ap))
apr := cli.ParseArgsOrDie(ap, args, help)
dropConflicts := apr.Contains(migrateDropConflictsFlag)
if err := MigrateDatabase(ctx, dEnv, dropConflicts); err != nil {
verr := errhand.BuildDError("migration failed").AddCause(err).Build()
return HandleVErrAndExitCode(verr, usage)
}
os.Exit(0)
return 0 // unreachable
}
// MigrateDatabase migrates the NomsBinFormat of |dEnv.DoltDB(ctx)|.
func MigrateDatabase(ctx context.Context, dEnv *env.DoltEnv, dropConflicts bool) error {
menv, err := migrate.NewEnvironment(ctx, dEnv)
if err != nil {
return err
}
menv.DropConflicts = dropConflicts
if curr := menv.Existing.DoltDB(ctx).Format(); types.IsFormat_DOLT(curr) {
cli.Println("database is already migrated")
return nil
}
p, err := menv.Migration.FS.Abs(".")
if err != nil {
return err
}
cli.Println("migrating database at tmp dir: ", p)
err = migrate.TraverseDAG(ctx, menv, menv.Existing.DoltDB(ctx), menv.Migration.DoltDB(ctx))
if err != nil {
return err
}
return migrate.SwapChunkStores(ctx, menv)
}
-2
View File
@@ -74,7 +74,6 @@ var commandsWithoutCliCtx = []cli.Command{
credcmds.Commands,
cvcmds.Commands,
commands.SendMetricsCmd{},
commands.MigrateCmd{},
indexcmds.Commands,
commands.ReadTablesCmd{},
commands.FilterBranchCmd{},
@@ -96,7 +95,6 @@ var commandsWithoutGlobalArgSupport = []cli.Command{
commands.InitCmd{},
commands.CloneCmd{},
docscmds.Commands,
commands.MigrateCmd{},
commands.ReadTablesCmd{},
commands.LoginCmd{},
credcmds.Commands,
-1
View File
@@ -68,7 +68,6 @@ var doltSubCommands = []cli.Command{
commands.BlameCmd{},
cvcmds.Commands,
commands.SendMetricsCmd{},
commands.MigrateCmd{},
indexcmds.Commands,
commands.ReadTablesCmd{},
commands.GarbageCollectionCmd{},
@@ -22,27 +22,11 @@ import (
"golang.org/x/sync/errgroup"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/utils/async"
"github.com/dolthub/dolt/go/store/diff"
"github.com/dolthub/dolt/go/store/types"
)
func NewRowDiffer(ctx context.Context, format *types.NomsBinFormat, fromSch, toSch schema.Schema, buf int) RowDiffer {
ad := NewAsyncDiffer(buf)
// Returns an EmptyRowDiffer if the two schemas are not diffable.
if !schema.ArePrimaryKeySetsDiffable(format, fromSch, toSch) {
return &EmptyRowDiffer{}
}
if schema.IsKeyless(fromSch) || schema.IsKeyless(toSch) {
return &keylessDiffer{AsyncDiffer: ad}
}
return ad
}
// todo: make package private
type AsyncDiffer struct {
diffChan chan diff.Difference
-111
View File
@@ -14,17 +14,6 @@
package diff
import (
"errors"
"io"
"time"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/rowconv"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/store/types"
)
const (
From = "from"
To = "to"
@@ -37,103 +26,3 @@ func ToColNamer(name string) string {
func FromColNamer(name string) string {
return From + "_" + name
}
type RowDiffSource struct {
ad RowDiffer
joiner *rowconv.Joiner
oldRowConv *rowconv.RowConverter
newRowConv *rowconv.RowConverter
warnFn rowconv.WarnFunction
}
func NewRowDiffSource(ad RowDiffer, joiner *rowconv.Joiner, warnFn rowconv.WarnFunction) *RowDiffSource {
return &RowDiffSource{
ad: ad,
joiner: joiner,
oldRowConv: rowconv.IdentityConverter,
newRowConv: rowconv.IdentityConverter,
warnFn: warnFn,
}
}
func (rdRd *RowDiffSource) AddInputRowConversion(oldConv, newConv *rowconv.RowConverter) {
rdRd.oldRowConv = oldConv
rdRd.newRowConv = newConv
}
// GetSchema gets the schema of the rows that this reader will return
func (rdRd *RowDiffSource) GetSchema() schema.Schema {
return rdRd.joiner.GetSchema()
}
// NextDiff reads a row from a table. If there is a bad row the returned error will be non nil, and calling IsBadRow(err)
// will be return true. This is a potentially non-fatal error and callers can decide if they want to continue on a bad row, or fail.
func (rdRd *RowDiffSource) NextDiff() (row.Row, error) {
diffs, hasMore, err := rdRd.ad.GetDiffs(1, time.Second)
if err != nil {
return nil, err
}
if len(diffs) == 0 {
if !hasMore {
return nil, io.EOF
}
return nil, errors.New("timeout")
}
if len(diffs) != 1 {
panic("only a single diff requested, multiple returned. bug in AsyncDiffer")
}
d := diffs[0]
rows := make(map[string]row.Row)
if d.OldValue != nil {
sch := rdRd.joiner.SchemaForName(From)
if !rdRd.oldRowConv.IdentityConverter {
sch = rdRd.oldRowConv.SrcSch
}
oldRow, err := row.FromNoms(sch, d.KeyValue.(types.Tuple), d.OldValue.(types.Tuple))
if err != nil {
return nil, err
}
rows[From], err = rdRd.oldRowConv.ConvertWithWarnings(oldRow, rdRd.warnFn)
if err != nil {
return nil, err
}
}
if d.NewValue != nil {
sch := rdRd.joiner.SchemaForName(To)
if !rdRd.newRowConv.IdentityConverter {
sch = rdRd.newRowConv.SrcSch
}
newRow, err := row.FromNoms(sch, d.KeyValue.(types.Tuple), d.NewValue.(types.Tuple))
if err != nil {
return nil, err
}
rows[To], err = rdRd.newRowConv.ConvertWithWarnings(newRow, rdRd.warnFn)
if err != nil {
return nil, err
}
}
joinedRow, err := rdRd.joiner.Join(rows)
if err != nil {
return nil, err
}
return joinedRow, nil
}
// Close should release resources being held
func (rdRd *RowDiffSource) Close() error {
rdRd.ad.Close()
return nil
}
+4 -38
View File
@@ -62,11 +62,8 @@ func Stat(ctx context.Context, ch chan DiffStatProgress, from, to durable.Index,
return fmt.Errorf("cannot perform a diff between keyless and keyed schema")
}
if types.IsFormat_DOLT(from.Format()) {
return diffProllyTrees(ctx, ch, keyless, from, to, fromSch, toSch)
}
return diffNomsMaps(ctx, ch, keyless, from, to, fromSch, toSch)
types.AssertFormat_DOLT(from.Format())
return diffProllyTrees(ctx, ch, keyless, from, to, fromSch, toSch)
}
// StatForTableDelta pushes diff stat progress messages for the table delta given to the channel given
@@ -88,7 +85,7 @@ func StatForTableDelta(ctx context.Context, ch chan DiffStatProgress, td TableDe
return errhand.BuildDError("cannot retrieve schema for table %s", td.ToName).AddCause(err).Build()
}
if !schema.ArePrimaryKeySetsDiffable(td.Format(), fromSch, toSch) {
if !schema.ArePrimaryKeySetsDiffable(fromSch, toSch) {
return fmt.Errorf("failed to compute diff stat for table %s: %w", td.CurName(), ErrPrimaryKeySetChanged)
}
@@ -102,11 +99,7 @@ func StatForTableDelta(ctx context.Context, ch chan DiffStatProgress, td TableDe
return err
}
if types.IsFormat_DOLT(td.Format()) {
return diffProllyTrees(ctx, ch, keyless, fromRows, toRows, fromSch, toSch)
} else {
return diffNomsMaps(ctx, ch, keyless, fromRows, toRows, fromSch, toSch)
}
return diffProllyTrees(ctx, ch, keyless, fromRows, toRows, fromSch, toSch)
}
func diffProllyTrees(ctx context.Context, ch chan DiffStatProgress, keyless bool, from, to durable.Index, fromSch, toSch schema.Schema) error {
@@ -175,33 +168,6 @@ func diffProllyTrees(ctx context.Context, ch chan DiffStatProgress, keyless bool
return nil
}
func diffNomsMaps(ctx context.Context, ch chan DiffStatProgress, keyless bool, fromRows durable.Index, toRows durable.Index, fromSch, toSch schema.Schema) error {
var rpr nomsReporter
if keyless {
rpr = reportNomsKeylessChanges
} else {
fc, err := fromRows.Count()
if err != nil {
return err
}
cfc := uint64(len(fromSch.GetAllCols().GetColumns())) * fc
tc, err := toRows.Count()
if err != nil {
return err
}
ctc := uint64(len(toSch.GetAllCols().GetColumns())) * tc
rpr = reportNomsPkChanges
ch <- DiffStatProgress{
OldRowSize: fc,
NewRowSize: tc,
OldCellSize: cfc,
NewCellSize: ctc,
}
}
return statWithReporter(ctx, ch, durable.NomsMapFromIndex(fromRows), durable.NomsMapFromIndex(toRows), rpr, fromSch, toSch)
}
func statWithReporter(ctx context.Context, ch chan DiffStatProgress, from, to types.Map, rpr nomsReporter, fromSch, toSch schema.Schema) (err error) {
ad := NewAsyncDiffer(1024)
ad.Start(ctx, from, to)
+1 -12
View File
@@ -594,7 +594,7 @@ func (td TableDelta) HasDataChanged(ctx context.Context) (bool, error) {
}
func (td TableDelta) HasPrimaryKeySetChanged() bool {
return !schema.ArePrimaryKeySetsDiffable(td.Format(), td.FromSch, td.ToSch)
return !schema.ArePrimaryKeySetsDiffable(td.FromSch, td.ToSch)
}
func (td TableDelta) HasChanges() (bool, error) {
@@ -658,17 +658,6 @@ func (td TableDelta) GetSchemas(ctx context.Context) (from, to schema.Schema, er
return td.FromSch, td.ToSch, nil
}
// Format returns the format of the tables in this delta.
func (td TableDelta) Format() *types.NomsBinFormat {
if td.FromRootObject != nil || td.ToRootObject != nil {
return types.Format_DOLT
}
if td.FromTable != nil {
return td.FromTable.Format()
}
return td.ToTable.Format()
}
func (td TableDelta) IsKeyless(ctx context.Context) (bool, error) {
f, t, err := td.GetSchemas(ctx)
if err != nil {
+1 -35
View File
@@ -27,7 +27,6 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/utils/filesys"
"github.com/dolthub/dolt/go/libraries/utils/test"
@@ -81,39 +80,6 @@ func CreateTestTable(vrw types.ValueReadWriter, ns tree.NodeStore, tSchema schem
return tbl, nil
}
func createTestRowData(t *testing.T, vrw types.ValueReadWriter, ns tree.NodeStore, sch schema.Schema) durable.Index {
if types.Format_Default == types.Format_DOLT {
idx, err := durable.NewEmptyPrimaryIndex(context.Background(), vrw, ns, sch)
require.NoError(t, err)
return idx
}
vals := []row.TaggedValues{
{idTag: types.UUID(id0), firstTag: types.String("bill"), lastTag: types.String("billerson"), ageTag: types.Uint(53)},
{idTag: types.UUID(id1), firstTag: types.String("eric"), lastTag: types.String("ericson"), isMarriedTag: types.Bool(true), ageTag: types.Uint(21)},
{idTag: types.UUID(id2), firstTag: types.String("john"), lastTag: types.String("johnson"), isMarriedTag: types.Bool(false), ageTag: types.Uint(53)},
{idTag: types.UUID(id3), firstTag: types.String("robert"), lastTag: types.String("robertson"), ageTag: types.Uint(36)},
}
var err error
rows := make([]row.Row, len(vals))
m, err := types.NewMap(context.Background(), vrw)
assert.NoError(t, err)
ed := m.Edit()
for i, val := range vals {
r, err := row.New(vrw.Format(), sch, val)
require.NoError(t, err)
rows[i] = r
ed = ed.Set(r.NomsMapKey(sch), r.NomsMapValue(sch))
}
m, err = ed.Map(context.Background())
assert.NoError(t, err)
return durable.IndexFromNomsMap(m, vrw, ns)
}
func TestIsValidTableName(t *testing.T) {
assert.True(t, IsValidTableName("a"))
assert.True(t, IsValidTableName("a1"))
@@ -271,7 +237,7 @@ func TestLDNoms(t *testing.T) {
}
}
//read the empty repo back and add a new table. Write the value, but don't commit
// read the empty repo back and add a new table. Write the value, but don't commit
var valHash hash.Hash
var tbl *Table
{
@@ -30,7 +30,6 @@ import (
type ArtifactIndex interface {
HashOf() (hash.Hash, error)
Count() (uint64, error)
Format() *types.NomsBinFormat
HasConflicts(ctx context.Context) (bool, error)
// ConflictCount returns the number of conflicts
ConflictCount(ctx context.Context) (uint64, error)
@@ -43,24 +42,15 @@ type ArtifactIndex interface {
// RefFromArtifactIndex persists |idx| and returns the types.Ref targeting it.
func RefFromArtifactIndex(ctx context.Context, vrw types.ValueReadWriter, idx ArtifactIndex) (types.Ref, error) {
switch idx.Format() {
case types.Format_LD_1:
panic("TODO")
case types.Format_DOLT:
b := shim.ValueFromMap(idx.(prollyArtifactIndex).index)
return refFromNomsValue(ctx, vrw, b)
default:
return types.Ref{}, errNbfUnknown
}
b := shim.ValueFromMap(idx.(prollyArtifactIndex).index)
return vrw.WriteValue(ctx, b)
}
// NewEmptyArtifactIndex returns an ArtifactIndex with no artifacts.
func NewEmptyArtifactIndex(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, tableSch schema.Schema) (ArtifactIndex, error) {
switch vrw.Format() {
case types.Format_LD_1:
panic("TODO")
panic("Unsupported format " + vrw.Format().VersionString())
case types.Format_DOLT:
kd := tableSch.GetKeyDescriptor(ns)
@@ -85,10 +75,6 @@ func ProllyMapFromArtifactIndex(i ArtifactIndex) prolly.ArtifactMap {
return i.(prollyArtifactIndex).index
}
func artifactIndexFromRef(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, tableSch schema.Schema, r types.Ref) (ArtifactIndex, error) {
return artifactIndexFromAddr(ctx, vrw, ns, tableSch, r.TargetHash())
}
func artifactIndexFromAddr(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, tableSch schema.Schema, addr hash.Hash) (ArtifactIndex, error) {
v, err := vrw.MustReadValue(ctx, addr)
if err != nil {
@@ -97,7 +83,7 @@ func artifactIndexFromAddr(ctx context.Context, vrw types.ValueReadWriter, ns tr
switch vrw.Format() {
case types.Format_LD_1:
panic("TODO")
panic("Unsupported format " + vrw.Format().VersionString())
case types.Format_DOLT:
root, fileId, err := shim.NodeFromValue(v)
@@ -129,10 +115,6 @@ func (i prollyArtifactIndex) Count() (uint64, error) {
return uint64(c), err
}
func (i prollyArtifactIndex) Format() *types.NomsBinFormat {
return i.index.Format()
}
func (i prollyArtifactIndex) HasConflicts(ctx context.Context) (bool, error) {
return i.index.HasArtifactOfType(ctx, prolly.ArtifactTypeConflict)
}
@@ -1,113 +0,0 @@
// Copyright 2022 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 durable
import (
"context"
"fmt"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/store/hash"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/types"
)
type ConflictIndex interface {
HashOf() (hash.Hash, error)
Count() uint64
Format() *types.NomsBinFormat
}
// RefFromConflictIndex persists |idx| and returns the types.Ref targeting it.
func RefFromConflictIndex(ctx context.Context, vrw types.ValueReadWriter, idx ConflictIndex) (types.Ref, error) {
switch idx.Format() {
case types.Format_LD_1:
return refFromNomsValue(ctx, vrw, idx.(nomsConflictIndex).index)
case types.Format_DOLT:
return types.Ref{}, fmt.Errorf("__DOLT__ conflicts should be stored in ArtifactIndex")
default:
return types.Ref{}, errNbfUnknown
}
}
// NewEmptyConflictIndex returns an ConflictIndex with no rows.
func NewEmptyConflictIndex(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, oursSch, theirsSch, baseSch schema.Schema) (ConflictIndex, error) {
switch vrw.Format() {
case types.Format_LD_1:
m, err := types.NewMap(ctx, vrw)
if err != nil {
return nil, err
}
return ConflictIndexFromNomsMap(m, vrw), nil
case types.Format_DOLT:
return nil, fmt.Errorf("__DOLT__ conflicts should be stored in ArtifactIndex")
default:
return nil, errNbfUnknown
}
}
func ConflictIndexFromNomsMap(m types.Map, vrw types.ValueReadWriter) ConflictIndex {
return nomsConflictIndex{
index: m,
vrw: vrw,
}
}
func NomsMapFromConflictIndex(i ConflictIndex) types.Map {
return i.(nomsConflictIndex).index
}
func conflictIndexFromRef(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, ourSch, theirSch, baseSch schema.Schema, r types.Ref) (ConflictIndex, error) {
return conflictIndexFromAddr(ctx, vrw, ns, ourSch, theirSch, baseSch, r.TargetHash())
}
func conflictIndexFromAddr(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, ourSch, theirSch, baseSch schema.Schema, addr hash.Hash) (ConflictIndex, error) {
v, err := vrw.ReadValue(ctx, addr)
if err != nil {
return nil, err
}
switch vrw.Format() {
case types.Format_LD_1:
return ConflictIndexFromNomsMap(v.(types.Map), vrw), nil
case types.Format_DOLT:
return nil, fmt.Errorf("__DOLT__ conflicts should be stored in ArtifactIndex")
default:
return nil, errNbfUnknown
}
}
type nomsConflictIndex struct {
index types.Map
vrw types.ValueReadWriter
}
func (i nomsConflictIndex) HashOf() (hash.Hash, error) {
return i.index.Hash(i.vrw.Format())
}
func (i nomsConflictIndex) Count() uint64 {
return i.index.Len()
}
func (i nomsConflictIndex) Format() *types.NomsBinFormat {
return i.vrw.Format()
}
+6 -180
View File
@@ -71,10 +71,6 @@ type IndexSet interface {
// PutIndex puts an index into the set.
PutIndex(ctx context.Context, name string, idx Index) (IndexSet, error)
// PutNomsIndex puts a noms index into the set.
// todo(andy): this is a temporary stop-gap while abstracting types.Map
PutNomsIndex(ctx context.Context, name string, idx types.Map) (IndexSet, error)
// DropIndex removes an index from the set.
DropIndex(ctx context.Context, name string) (IndexSet, error)
@@ -86,11 +82,10 @@ type IndexSet interface {
func RefFromIndex(ctx context.Context, vrw types.ValueReadWriter, idx Index) (types.Ref, error) {
switch idx.Format() {
case types.Format_LD_1:
return refFromNomsValue(ctx, vrw, idx.(nomsIndex).index)
panic("Unsupported format " + idx.Format().VersionString())
case types.Format_DOLT:
b := shim.ValueFromMap(MapFromIndex(idx))
return refFromNomsValue(ctx, vrw, b)
return vrw.WriteValue(ctx, b)
default:
return types.Ref{}, errNbfUnknown
@@ -111,7 +106,7 @@ func indexFromAddr(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeS
switch vrw.Format() {
case types.Format_LD_1:
return IndexFromNomsMap(v.(types.Map), vrw, ns), nil
panic("Unsupported format " + vrw.Format().VersionString())
case types.Format_DOLT:
m, err := shim.MapInterfaceFromValue(ctx, v, sch, ns, isKeylessTable)
@@ -146,11 +141,7 @@ func NewEmptyIndexFromTableSchema(ctx context.Context, vrw types.ValueReadWriter
func newEmptyIndex(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, sch schema.Schema, isVector bool, isKeylessSecondary bool) (Index, error) {
switch vrw.Format() {
case types.Format_LD_1:
m, err := types.NewMap(ctx, vrw)
if err != nil {
return nil, err
}
return IndexFromNomsMap(m, vrw, ns), nil
panic("Unsupported format " + vrw.Format().VersionString())
case types.Format_DOLT:
kd, vd := sch.GetMapDescriptors(ns)
@@ -188,14 +179,6 @@ func NewEmptyProximityIndex(ctx context.Context, ns tree.NodeStore, kd, vd *val.
return IndexFromProximityMap(m), nil
}
type nomsIndex struct {
index types.Map
vrw types.ValueReadWriter
ns tree.NodeStore
}
var _ Index = nomsIndex{}
func IterAllIndexes(
ctx context.Context,
sch schema.Schema,
@@ -214,60 +197,6 @@ func IterAllIndexes(
return nil
}
// NomsMapFromIndex unwraps the Index and returns the underlying types.Map.
func NomsMapFromIndex(i Index) types.Map {
return i.(nomsIndex).index
}
// IndexFromNomsMap wraps a types.Map and returns it as an Index.
func IndexFromNomsMap(m types.Map, vrw types.ValueReadWriter, ns tree.NodeStore) Index {
return nomsIndex{
index: m,
vrw: vrw,
ns: ns,
}
}
var _ Index = nomsIndex{}
// HashOf implements Index.
func (i nomsIndex) HashOf() (hash.Hash, error) {
return i.index.Hash(i.vrw.Format())
}
// Count implements Index.
func (i nomsIndex) Count() (uint64, error) {
return i.index.Len(), nil
}
// Empty implements Index.
func (i nomsIndex) Empty() (bool, error) {
return i.index.Len() == 0, nil
}
// Format implements Index.
func (i nomsIndex) Format() *types.NomsBinFormat {
return i.vrw.Format()
}
// bytes implements Index.
func (i nomsIndex) bytes() ([]byte, error) {
rowschunk, err := types.EncodeValue(i.index, i.vrw.Format())
if err != nil {
return nil, err
}
return rowschunk.Data(), nil
}
func (i nomsIndex) AddColumnToRows(ctx context.Context, newCol string, newSchema schema.Schema) (Index, error) {
// no-op for noms indexes because of tag-based mapping
return i, nil
}
func (i nomsIndex) DebugString(ctx context.Context, ns tree.NodeStore, schema schema.Schema) string {
panic("Not implemented")
}
type prollyIndex struct {
index prolly.Map
}
@@ -432,14 +361,7 @@ func NewIndexSet(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeSto
return doltDevIndexSet{vrw, ns, emptyam}, nil
}
empty, err := types.NewMap(ctx, vrw)
if err != nil {
return nil, err
}
return nomsIndexSet{
indexes: empty,
vrw: vrw,
}, nil
panic("Unsupported format " + vrw.Format().VersionString())
}
func NewIndexSetWithEmptyIndexes(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, sch schema.Schema) (IndexSet, error) {
@@ -460,98 +382,6 @@ func NewIndexSetWithEmptyIndexes(ctx context.Context, vrw types.ValueReadWriter,
return s, nil
}
type nomsIndexSet struct {
indexes types.Map
vrw types.ValueReadWriter
ns tree.NodeStore
}
var _ IndexSet = nomsIndexSet{}
// HashOf implements IndexSet.
func (s nomsIndexSet) HashOf() (hash.Hash, error) {
return s.indexes.Hash(s.vrw.Format())
}
// HasIndex implements IndexSet.
func (s nomsIndexSet) HasIndex(ctx context.Context, name string) (bool, error) {
_, ok, err := s.indexes.MaybeGet(ctx, types.String(name))
if err != nil {
return false, err
}
return ok, nil
}
// GetIndex implements IndexSet.
func (s nomsIndexSet) GetIndex(ctx context.Context, tableSch schema.Schema, idxSch schema.Schema, name string) (Index, error) {
v, ok, err := s.indexes.MaybeGet(ctx, types.String(name))
if !ok {
err = fmt.Errorf("index %s not found in IndexSet", name)
}
if err != nil {
return nil, err
}
idx := tableSch.Indexes().GetByName(name)
if idx == nil {
return nil, fmt.Errorf("index not found: %s", name)
}
return indexFromRef(ctx, s.vrw, s.ns, idxSch, v.(types.Ref))
}
// PutNomsIndex implements IndexSet.
func (s nomsIndexSet) PutNomsIndex(ctx context.Context, name string, idx types.Map) (IndexSet, error) {
return s.PutIndex(ctx, name, IndexFromNomsMap(idx, s.vrw, s.ns))
}
// PutIndex implements IndexSet.
func (s nomsIndexSet) PutIndex(ctx context.Context, name string, idx Index) (IndexSet, error) {
ref, err := RefFromIndex(ctx, s.vrw, idx)
if err != nil {
return nil, err
}
im, err := s.indexes.Edit().Set(types.String(name), ref).Map(ctx)
if err != nil {
return nil, err
}
return nomsIndexSet{indexes: im, vrw: s.vrw, ns: s.ns}, nil
}
// DropIndex implements IndexSet.
func (s nomsIndexSet) DropIndex(ctx context.Context, name string) (IndexSet, error) {
im, err := s.indexes.Edit().Remove(types.String(name)).Map(ctx)
if err != nil {
return nil, err
}
return nomsIndexSet{indexes: im, vrw: s.vrw, ns: s.ns}, nil
}
func (s nomsIndexSet) RenameIndex(ctx context.Context, oldName, newName string) (IndexSet, error) {
v, ok, err := s.indexes.MaybeGet(ctx, types.String(oldName))
if !ok {
err = fmt.Errorf("index %s not found in IndexSet", oldName)
}
if err != nil {
return nil, err
}
edit := s.indexes.Edit()
im, err := edit.Set(types.String(newName), v).Remove(types.String(oldName)).Map(ctx)
if err != nil {
return nil, err
}
return nomsIndexSet{indexes: im, vrw: s.vrw, ns: s.ns}, nil
}
func mapFromIndexSet(ic IndexSet) types.Map {
return ic.(nomsIndexSet).indexes
}
type doltDevIndexSet struct {
vrw types.ValueReadWriter
ns tree.NodeStore
@@ -604,11 +434,7 @@ func (is doltDevIndexSet) PutIndex(ctx context.Context, name string, idx Index)
return nil, err
}
return doltDevIndexSet{is.vrw, is.ns, am}, nil
}
func (is doltDevIndexSet) PutNomsIndex(ctx context.Context, name string, idx types.Map) (IndexSet, error) {
return is.PutIndex(ctx, name, IndexFromNomsMap(idx, is.vrw, is.ns))
return doltDevIndexSet{vrw: is.vrw, ns: is.ns, am: am}, nil
}
func (is doltDevIndexSet) DropIndex(ctx context.Context, name string) (IndexSet, error) {
+26 -738
View File
@@ -15,7 +15,6 @@
package durable
import (
"bytes"
"context"
"errors"
"fmt"
@@ -23,7 +22,6 @@ import (
flatbuffers "github.com/dolthub/flatbuffers/v23/go"
"github.com/dolthub/dolt/go/gen/fb/serial"
"github.com/dolthub/dolt/go/libraries/doltcore/conflict"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/schema/encoding"
"github.com/dolthub/dolt/go/store/hash"
@@ -35,26 +33,8 @@ import (
"github.com/dolthub/dolt/go/store/val"
)
const (
tableStructName = "table"
schemaRefKey = "schema_ref"
tableRowsKey = "rows"
artifactsKey = "artifacts"
conflictsKey = "conflicts"
conflictSchemasKey = "conflict_schemas"
constraintViolationsKey = "constraint_violations"
indexesKey = "indexes"
autoIncrementKey = "auto_increment"
)
var (
ErrUnknownAutoIncrementValue = fmt.Errorf("auto increment set for non-numeric column type")
)
var (
errNbfUnknown = fmt.Errorf("unknown NomsBinFormat")
errNbfUnsupported = fmt.Errorf("operation unsupported for NomsBinFormat")
errNbfUnknown = fmt.Errorf("unknown NomsBinFormat")
)
// Table is a Dolt table that can be persisted.
@@ -62,9 +42,6 @@ type Table interface {
// HashOf returns the hash.Hash of this table.
HashOf() (hash.Hash, error)
// Format returns the types.NomsBinFormat for this table.
Format() *types.NomsBinFormat
// GetSchemaHash returns the hash.Hash of this table's schema.
GetSchemaHash(ctx context.Context) (hash.Hash, error)
// GetSchema returns this table's schema.
@@ -89,20 +66,6 @@ type Table interface {
// SetArtifacts sets the merge artifacts for this table.
SetArtifacts(ctx context.Context, artifacts ArtifactIndex) (Table, error)
// GetConflicts returns the merge conflicts for this table.
GetConflicts(ctx context.Context) (conflict.ConflictSchema, ConflictIndex, error)
// HasConflicts returns true if this table has conflicts.
HasConflicts(ctx context.Context) (bool, error)
// SetConflicts sets the merge conflicts for this table.
SetConflicts(ctx context.Context, sch conflict.ConflictSchema, conflicts ConflictIndex) (Table, error)
// ClearConflicts removes all merge conflicts for this table.
ClearConflicts(ctx context.Context) (Table, error)
// GetConstraintViolations returns the constraint violations for this table.
GetConstraintViolations(ctx context.Context) (types.Map, error)
// SetConstraintViolations sets the constraint violations for this table.
SetConstraintViolations(ctx context.Context, violations types.Map) (Table, error)
// GetAutoIncrement returns the AUTO_INCREMENT sequence value for this table.
GetAutoIncrement(ctx context.Context) (uint64, error)
// SetAutoIncrement sets the AUTO_INCREMENT sequence value for this table.
@@ -112,574 +75,52 @@ type Table interface {
DebugString(ctx context.Context, ns tree.NodeStore) string
}
type nomsTable struct {
vrw types.ValueReadWriter
ns tree.NodeStore
tableStruct types.Struct
}
var _ Table = nomsTable{}
var sharePool = pool.NewBuffPool()
// NewNomsTable makes a new Table.
func NewNomsTable(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, sch schema.Schema, rows types.Map, indexes IndexSet, autoIncVal types.Value) (Table, error) {
return NewTable(ctx, vrw, ns, sch, nomsIndex{index: rows, vrw: vrw}, indexes, autoIncVal)
}
// NewTable returns a new Table.
func NewTable(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, sch schema.Schema, rows Index, indexes IndexSet, autoIncVal types.Value) (Table, error) {
if vrw.Format().UsesFlatbuffers() {
return newDoltDevTable(ctx, vrw, ns, sch, rows, indexes, autoIncVal)
}
schVal, err := encoding.MarshalSchema(ctx, vrw, sch)
if err != nil {
return nil, err
}
schemaRef, err := refFromNomsValue(ctx, vrw, schVal)
if err != nil {
return nil, err
}
rowsRef, err := RefFromIndex(ctx, vrw, rows)
if err != nil {
return nil, err
}
if indexes == nil {
indexes, err = NewIndexSet(ctx, vrw, ns)
if err != nil {
return nil, err
}
}
indexesRef, err := refFromNomsValue(ctx, vrw, mapFromIndexSet(indexes))
if err != nil {
return nil, err
}
sd := types.StructData{
schemaRefKey: schemaRef,
tableRowsKey: rowsRef,
indexesKey: indexesRef,
}
if schema.HasAutoIncrement(sch) && autoIncVal != nil {
sd[autoIncrementKey] = autoIncVal
}
tableStruct, err := types.NewStruct(vrw.Format(), tableStructName, sd)
if err != nil {
return nil, err
}
return nomsTable{vrw, ns, tableStruct}, nil
panic("Unsupported format: " + vrw.Format().VersionString())
}
// TableFromAddr deserializes the table in the chunk at |addr|.
func TableFromAddr(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, addr hash.Hash) (Table, error) {
types.AssertFormat_DOLT(vrw.Format())
val, err := vrw.MustReadValue(ctx, addr)
if err != nil {
return nil, err
}
if !vrw.Format().UsesFlatbuffers() {
st, ok := val.(types.Struct)
if !ok {
err = errors.New("table ref is unexpected noms value")
return nil, err
}
return nomsTable{vrw: vrw, tableStruct: st, ns: ns}, nil
} else {
sm, ok := val.(types.SerialMessage)
if !ok {
err = errors.New("table ref is unexpected noms value; not SerialMessage")
return nil, err
}
id := serial.GetFileID(sm)
if id != serial.TableFileID {
err = errors.New("table ref is unexpected noms value; GetFileID == " + id)
return nil, err
}
st, err := serial.TryGetRootAsTable([]byte(sm), serial.MessagePrefixSz)
if err != nil {
return nil, err
}
return doltDevTable{vrw, ns, st}, nil
sm, ok := val.(types.SerialMessage)
if !ok {
err = errors.New("table ref is unexpected noms value; not SerialMessage")
return nil, err
}
}
// RefFromNomsTable serialized |table|, and returns its types.Ref.
func RefFromNomsTable(ctx context.Context, table Table) (types.Ref, error) {
nt, ok := table.(nomsTable)
if ok {
return refFromNomsValue(ctx, nt.vrw, nt.tableStruct)
id := serial.GetFileID(sm)
if id != serial.TableFileID {
err = errors.New("table ref is unexpected noms value; GetFileID == " + id)
return nil, err
}
ddt := table.(doltDevTable)
return refFromNomsValue(ctx, ddt.vrw, ddt.nomsValue())
st, err := serial.TryGetRootAsTable([]byte(sm), serial.MessagePrefixSz)
if err != nil {
return nil, err
}
return doltDevTable{vrw, ns, st}, nil
}
// VrwFromTable returns the types.ValueReadWriter used by |t|.
// todo(andy): this is a temporary method that will be removed when there is a
// general-purpose abstraction to replace types.ValueReadWriter.
func VrwFromTable(t Table) types.ValueReadWriter {
if nt, ok := t.(nomsTable); ok {
return nt.vrw
} else {
ddt := t.(doltDevTable)
return ddt.vrw
}
ddt := t.(doltDevTable)
return ddt.vrw
}
func NodeStoreFromTable(t Table) tree.NodeStore {
if nt, ok := t.(nomsTable); ok {
return nt.ns
} else {
ddt := t.(doltDevTable)
return ddt.ns
}
}
// valueReadWriter returns the valueReadWriter for this table.
func (t nomsTable) valueReadWriter() types.ValueReadWriter {
return t.vrw
}
// HashOf implements Table.
func (t nomsTable) HashOf() (hash.Hash, error) {
return t.tableStruct.Hash(t.vrw.Format())
}
// Format returns the types.NomsBinFormat for this index.
func (t nomsTable) Format() *types.NomsBinFormat {
return t.vrw.Format()
}
// GetSchema implements Table.
func (t nomsTable) GetSchema(ctx context.Context) (schema.Schema, error) {
schemaRefVal, _, err := t.tableStruct.MaybeGet(schemaRefKey)
if err != nil {
return nil, err
}
schemaRef := schemaRefVal.(types.Ref)
return schemaFromRef(ctx, t.vrw, schemaRef)
}
// GetSchemaHash implements Table.
func (t nomsTable) GetSchemaHash(ctx context.Context) (hash.Hash, error) {
r, _, err := t.tableStruct.MaybeGet(schemaRefKey)
if err != nil {
return hash.Hash{}, err
}
return r.(types.Ref).TargetHash(), nil
}
// SetSchema implements Table.
func (t nomsTable) SetSchema(ctx context.Context, sch schema.Schema) (Table, error) {
newSchemaVal, err := encoding.MarshalSchema(ctx, t.vrw, sch)
if err != nil {
return nil, err
}
schRef, err := refFromNomsValue(ctx, t.vrw, newSchemaVal)
if err != nil {
return nil, err
}
newTableStruct, err := t.tableStruct.Set(schemaRefKey, schRef)
if err != nil {
return nil, err
}
return nomsTable{t.vrw, t.ns, newTableStruct}, nil
}
// SetTableRows implements Table.
func (t nomsTable) SetTableRows(ctx context.Context, updatedRows Index) (Table, error) {
rowsRef, err := RefFromIndex(ctx, t.vrw, updatedRows)
if err != nil {
return nil, err
}
updatedSt, err := t.tableStruct.Set(tableRowsKey, rowsRef)
if err != nil {
return nil, err
}
return nomsTable{t.vrw, t.ns, updatedSt}, nil
}
// GetTableRows implements Table.
func (t nomsTable) GetTableRows(ctx context.Context) (Index, error) {
val, _, err := t.tableStruct.MaybeGet(tableRowsKey)
if err != nil {
return nil, err
}
sch, err := t.GetSchema(ctx)
if err != nil {
return nil, err
}
return indexFromRef(ctx, t.vrw, t.ns, sch, val.(types.Ref))
}
func (t nomsTable) GetTableRowsWithDescriptors(ctx context.Context, kd, vd *val.TupleDesc) (Index, error) {
return nil, fmt.Errorf("nomsTable does not implement GetTableRowsWithDescriptors")
}
// GetIndexes implements Table.
func (t nomsTable) GetIndexes(ctx context.Context) (IndexSet, error) {
iv, ok, err := t.tableStruct.MaybeGet(indexesKey)
if err != nil {
return nil, err
}
if !ok {
return NewIndexSet(ctx, t.vrw, t.ns)
}
im, err := iv.(types.Ref).TargetValue(ctx, t.vrw)
if err != nil {
return nil, err
}
return nomsIndexSet{
indexes: im.(types.Map),
vrw: t.vrw,
ns: t.ns,
}, nil
}
// SetIndexes implements Table.
func (t nomsTable) SetIndexes(ctx context.Context, indexes IndexSet) (Table, error) {
if indexes == nil {
var err error
indexes, err = NewIndexSet(ctx, t.vrw, t.ns)
if err != nil {
return nil, err
}
}
indexesRef, err := refFromNomsValue(ctx, t.vrw, mapFromIndexSet(indexes))
if err != nil {
return nil, err
}
newTableStruct, err := t.tableStruct.Set(indexesKey, indexesRef)
if err != nil {
return nil, err
}
return nomsTable{t.vrw, t.ns, newTableStruct}, nil
}
// GetArtifacts implements Table.
func (t nomsTable) GetArtifacts(ctx context.Context) (ArtifactIndex, error) {
if t.Format() != types.Format_DOLT {
panic("artifacts not implemented for old storage format")
}
sch, err := t.GetSchema(ctx)
if err != nil {
return nil, err
}
val, ok, err := t.tableStruct.MaybeGet(artifactsKey)
if err != nil {
return nil, err
}
if !ok {
return NewEmptyArtifactIndex(ctx, t.vrw, t.ns, sch)
}
return artifactIndexFromRef(ctx, t.vrw, t.ns, sch, val.(types.Ref))
}
// SetArtifacts implements Table.
func (t nomsTable) SetArtifacts(ctx context.Context, artifacts ArtifactIndex) (Table, error) {
if t.Format() != types.Format_DOLT {
panic("artifacts not implemented for old storage format")
}
ref, err := RefFromArtifactIndex(ctx, t.vrw, artifacts)
if err != nil {
return nil, err
}
updated, err := t.tableStruct.Set(artifactsKey, ref)
if err != nil {
return nil, err
}
return nomsTable{t.vrw, t.ns, updated}, nil
}
// HasConflicts implements Table.
func (t nomsTable) HasConflicts(ctx context.Context) (bool, error) {
_, ok, err := t.tableStruct.MaybeGet(conflictSchemasKey)
return ok, err
}
// GetConflicts implements Table.
func (t nomsTable) GetConflicts(ctx context.Context) (conflict.ConflictSchema, ConflictIndex, error) {
schemasVal, ok, err := t.tableStruct.MaybeGet(conflictSchemasKey)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
if !ok {
sch, err := t.GetSchema(ctx)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
empty, err := NewEmptyConflictIndex(ctx, t.vrw, t.ns, sch, sch, sch)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
return conflict.ConflictSchema{}, empty, nil
}
schemas, err := conflict.ConflictSchemaFromValue(ctx, t.vrw, schemasVal)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
conflictsVal, _, err := t.tableStruct.MaybeGet(conflictsKey)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
if conflictsVal == nil {
confIndex, err := NewEmptyConflictIndex(ctx, t.vrw, t.ns, schemas.Schema, schemas.MergeSchema, schemas.Base)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
return conflict.ConflictSchema{}, confIndex, nil
}
i, err := conflictIndexFromRef(ctx, t.vrw, t.ns, schemas.Schema, schemas.MergeSchema, schemas.Base, conflictsVal.(types.Ref))
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
return schemas, i, nil
}
// SetConflicts implements Table.
func (t nomsTable) SetConflicts(ctx context.Context, schemas conflict.ConflictSchema, conflictData ConflictIndex) (Table, error) {
if t.Format() == types.Format_DOLT {
panic("should use artifacts")
}
conflictsRef, err := RefFromConflictIndex(ctx, t.vrw, conflictData)
if err != nil {
return nil, err
}
tpl, err := conflict.ValueFromConflictSchema(ctx, t.vrw, schemas)
if err != nil {
return nil, err
}
updatedSt, err := t.tableStruct.Set(conflictSchemasKey, tpl)
if err != nil {
return nil, err
}
updatedSt, err = updatedSt.Set(conflictsKey, conflictsRef)
if err != nil {
return nil, err
}
return nomsTable{t.vrw, t.ns, updatedSt}, nil
}
// GetConflictSchemas implements Table.
func (t nomsTable) GetConflictSchemas(ctx context.Context) (base, sch, mergeSch schema.Schema, err error) {
schemasVal, ok, err := t.tableStruct.MaybeGet(conflictSchemasKey)
if err != nil {
return nil, nil, nil, err
}
if ok {
schemas, err := conflict.ConflictFromTuple(schemasVal.(types.Tuple))
if err != nil {
return nil, nil, nil, err
}
baseRef := schemas.Base.(types.Ref)
valRef := schemas.Value.(types.Ref)
mergeRef := schemas.MergeValue.(types.Ref)
var baseSch, sch, mergeSch schema.Schema
if baseSch, err = schemaFromRef(ctx, t.vrw, baseRef); err == nil {
if sch, err = schemaFromRef(ctx, t.vrw, valRef); err == nil {
mergeSch, err = schemaFromRef(ctx, t.vrw, mergeRef)
}
}
return baseSch, sch, mergeSch, err
}
return nil, nil, nil, nil
}
// ClearConflicts implements Table.
func (t nomsTable) ClearConflicts(ctx context.Context) (Table, error) {
if t.Format() == types.Format_DOLT {
panic("should use artifacts")
}
tSt, err := t.tableStruct.Delete(conflictSchemasKey)
if err != nil {
return nil, err
}
tSt, err = tSt.Delete(conflictsKey)
if err != nil {
return nil, err
}
return nomsTable{t.vrw, t.ns, tSt}, nil
}
// GetConstraintViolations implements Table.
func (t nomsTable) GetConstraintViolations(ctx context.Context) (types.Map, error) {
constraintViolationsRefVal, ok, err := t.tableStruct.MaybeGet(constraintViolationsKey)
if err != nil {
return types.EmptyMap, err
}
if !ok {
emptyMap, err := types.NewMap(ctx, t.vrw)
return emptyMap, err
}
constraintViolationsVal, err := constraintViolationsRefVal.(types.Ref).TargetValue(ctx, t.vrw)
if err != nil {
return types.EmptyMap, err
}
return constraintViolationsVal.(types.Map), nil
}
// SetConstraintViolations implements Table.
func (t nomsTable) SetConstraintViolations(ctx context.Context, violationsMap types.Map) (Table, error) {
// We can't just call violationsMap.Empty() as we can't guarantee that the caller passed in an instantiated map
if violationsMap == types.EmptyMap || violationsMap.Len() == 0 {
updatedStruct, err := t.tableStruct.Delete(constraintViolationsKey)
if err != nil {
return nil, err
}
return nomsTable{t.vrw, t.ns, updatedStruct}, nil
}
constraintViolationsRef, err := refFromNomsValue(ctx, t.vrw, violationsMap)
if err != nil {
return nil, err
}
updatedStruct, err := t.tableStruct.Set(constraintViolationsKey, constraintViolationsRef)
if err != nil {
return nil, err
}
return nomsTable{t.vrw, t.ns, updatedStruct}, nil
}
// GetAutoIncrement implements Table.
func (t nomsTable) GetAutoIncrement(ctx context.Context) (uint64, error) {
val, ok, err := t.tableStruct.MaybeGet(autoIncrementKey)
if err != nil {
return 0, err
}
if !ok {
return 1, nil
}
// older versions might have serialized auto-increment
// value as types.Int or types.Float.
switch t := val.(type) {
case types.Int:
return uint64(t), nil
case types.Uint:
return uint64(t), nil
case types.Float:
return uint64(t), nil
default:
return 0, ErrUnknownAutoIncrementValue
}
}
// SetAutoIncrement implements Table.
func (t nomsTable) SetAutoIncrement(ctx context.Context, val uint64) (Table, error) {
st, err := t.tableStruct.Set(autoIncrementKey, types.Uint(val))
if err != nil {
return nil, err
}
return nomsTable{t.vrw, t.ns, st}, nil
}
func (t nomsTable) DebugString(ctx context.Context, ns tree.NodeStore) string {
var buf bytes.Buffer
err := types.WriteEncodedValue(ctx, &buf, t.tableStruct)
if err != nil {
panic(err)
}
schemaRefVal, _, _ := t.tableStruct.MaybeGet(schemaRefKey)
schemaRef := schemaRefVal.(types.Ref)
schemaVal, err := schemaRef.TargetValue(ctx, t.vrw)
if err != nil {
panic(err)
}
buf.WriteString("\nschema: ")
err = types.WriteEncodedValue(ctx, &buf, schemaVal)
if err != nil {
panic(err)
}
iv, ok, err := t.tableStruct.MaybeGet(indexesKey)
if err != nil {
panic(err)
}
if ok {
buf.WriteString("\nindexes: ")
im, err := iv.(types.Ref).TargetValue(ctx, t.vrw)
if err != nil {
panic(err)
}
err = types.WriteEncodedValue(ctx, &buf, im)
if err != nil {
panic(err)
}
}
buf.WriteString("\ndata:\n")
data, err := t.GetTableRows(ctx)
if err != nil {
panic(err)
}
err = types.WriteEncodedValue(ctx, &buf, NomsMapFromIndex(data))
if err != nil {
panic(err)
}
return buf.String()
}
func refFromNomsValue(ctx context.Context, vrw types.ValueReadWriter, val types.Value) (types.Ref, error) {
return vrw.WriteValue(ctx, val)
}
func schemaFromRef(ctx context.Context, vrw types.ValueReadWriter, ref types.Ref) (schema.Schema, error) {
return schemaFromAddr(ctx, vrw, ref.TargetHash())
ddt := t.(doltDevTable)
return ddt.ns
}
func schemaFromAddr(ctx context.Context, vrw types.ValueReadWriter, addr hash.Hash) (schema.Schema, error) {
@@ -763,7 +204,7 @@ func newDoltDevTable(ctx context.Context, vrw types.ValueReadWriter, ns tree.Nod
return nil, err
}
schemaRef, err := refFromNomsValue(ctx, vrw, schVal)
schemaRef, err := vrw.WriteValue(ctx, schVal)
if err != nil {
return nil, err
}
@@ -833,7 +274,7 @@ func (t doltDevTable) SetSchema(ctx context.Context, sch schema.Schema) (Table,
return nil, err
}
schRef, err := refFromNomsValue(ctx, t.vrw, newSchemaVal)
schRef, err := t.vrw.WriteValue(ctx, newSchemaVal)
if err != nil {
return nil, err
}
@@ -915,64 +356,6 @@ func (t doltDevTable) SetIndexes(ctx context.Context, indexes IndexSet) (Table,
return doltDevTable{t.vrw, t.ns, msg}, nil
}
func (t doltDevTable) GetConflicts(ctx context.Context) (conflict.ConflictSchema, ConflictIndex, error) {
conflicts, err := t.msg.TryConflicts(nil)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
ouraddr := hash.New(conflicts.OurSchemaBytes())
theiraddr := hash.New(conflicts.TheirSchemaBytes())
baseaddr := hash.New(conflicts.AncestorSchemaBytes())
if ouraddr.IsEmpty() {
sch, err := t.GetSchema(ctx)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
empty, err := NewEmptyConflictIndex(ctx, t.vrw, t.ns, sch, sch, sch)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
return conflict.ConflictSchema{}, empty, nil
}
ourschema, err := getSchemaAtAddr(ctx, t.vrw, ouraddr)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
theirschema, err := getSchemaAtAddr(ctx, t.vrw, theiraddr)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
baseschema, err := getSchemaAtAddr(ctx, t.vrw, baseaddr)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
conflictschema := conflict.ConflictSchema{
Base: baseschema,
Schema: ourschema,
MergeSchema: theirschema,
}
mapaddr := hash.New(conflicts.DataBytes())
var conflictIdx ConflictIndex
if mapaddr.IsEmpty() {
conflictIdx, err = NewEmptyConflictIndex(ctx, t.vrw, t.ns, ourschema, theirschema, baseschema)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
} else {
conflictIdx, err = conflictIndexFromAddr(ctx, t.vrw, t.ns, ourschema, theirschema, baseschema, mapaddr)
if err != nil {
return conflict.ConflictSchema{}, nil, err
}
}
return conflictschema, conflictIdx, nil
}
// GetArtifacts implements Table.
func (t doltDevTable) GetArtifacts(ctx context.Context) (ArtifactIndex, error) {
if t.Format() != types.Format_DOLT {
@@ -1017,90 +400,6 @@ func (t doltDevTable) SetArtifacts(ctx context.Context, artifacts ArtifactIndex)
return doltDevTable{t.vrw, t.ns, msg}, nil
}
func (t doltDevTable) HasConflicts(ctx context.Context) (bool, error) {
conflicts, err := t.msg.TryConflicts(nil)
if err != nil {
return false, err
}
addr := hash.New(conflicts.OurSchemaBytes())
return !addr.IsEmpty(), nil
}
func (t doltDevTable) SetConflicts(ctx context.Context, sch conflict.ConflictSchema, conflicts ConflictIndex) (Table, error) {
conflictsRef, err := RefFromConflictIndex(ctx, t.vrw, conflicts)
if err != nil {
return nil, err
}
conflictsAddr := conflictsRef.TargetHash()
baseaddr, err := getAddrForSchema(ctx, t.vrw, sch.Base)
if err != nil {
return nil, err
}
ouraddr, err := getAddrForSchema(ctx, t.vrw, sch.Schema)
if err != nil {
return nil, err
}
theiraddr, err := getAddrForSchema(ctx, t.vrw, sch.MergeSchema)
if err != nil {
return nil, err
}
msg := t.clone()
cmsg, err := msg.TryConflicts(nil)
if err != nil {
return nil, err
}
copy(cmsg.DataBytes(), conflictsAddr[:])
copy(cmsg.OurSchemaBytes(), ouraddr[:])
copy(cmsg.TheirSchemaBytes(), theiraddr[:])
copy(cmsg.AncestorSchemaBytes(), baseaddr[:])
return doltDevTable{t.vrw, t.ns, msg}, nil
}
func (t doltDevTable) ClearConflicts(ctx context.Context) (Table, error) {
msg := t.clone()
conflicts, err := msg.TryConflicts(nil)
if err != nil {
return nil, err
}
var emptyhash hash.Hash
copy(conflicts.DataBytes(), emptyhash[:])
copy(conflicts.OurSchemaBytes(), emptyhash[:])
copy(conflicts.TheirSchemaBytes(), emptyhash[:])
copy(conflicts.AncestorSchemaBytes(), emptyhash[:])
return doltDevTable{t.vrw, t.ns, msg}, nil
}
func (t doltDevTable) GetConstraintViolations(ctx context.Context) (types.Map, error) {
addr := hash.New(t.msg.ViolationsBytes())
if addr.IsEmpty() {
return types.NewMap(ctx, t.vrw)
}
v, err := t.vrw.MustReadValue(ctx, addr)
if err != nil {
return types.Map{}, err
}
return v.(types.Map), nil
}
func (t doltDevTable) SetConstraintViolations(ctx context.Context, violations types.Map) (Table, error) {
var addr hash.Hash
if violations != types.EmptyMap && violations.Len() != 0 {
ref, err := refFromNomsValue(ctx, t.vrw, violations)
if err != nil {
return nil, err
}
addr = ref.TargetHash()
}
msg := t.clone()
copy(msg.ViolationsBytes(), addr[:])
return doltDevTable{t.vrw, t.ns, msg}, nil
}
// GetAutoIncrement returns the next value to be used for the AUTO_INCREMENT column.
func (t doltDevTable) GetAutoIncrement(ctx context.Context) (uint64, error) {
res := t.msg.AutoIncrementValue()
@@ -1175,18 +474,7 @@ func (t doltDevTable) fields() (serialTableFields, error) {
}, nil
}
func getSchemaAtAddr(ctx context.Context, vrw types.ValueReadWriter, addr hash.Hash) (schema.Schema, error) {
return encoding.UnmarshalSchemaAtAddr(ctx, vrw, addr)
}
func getAddrForSchema(ctx context.Context, vrw types.ValueReadWriter, sch schema.Schema) (hash.Hash, error) {
st, err := encoding.MarshalSchema(ctx, vrw, sch)
if err != nil {
return hash.Hash{}, err
}
ref, err := vrw.WriteValue(ctx, st)
if err != nil {
return hash.Hash{}, err
}
return ref.TargetHash(), nil
func RefFromNomsTable(ctx context.Context, table Table) (types.Ref, error) {
ddt := table.(doltDevTable)
return ddt.vrw.WriteValue(ctx, ddt.nomsValue())
}
+6 -207
View File
@@ -20,7 +20,6 @@ import (
"fmt"
"unicode"
"github.com/dolthub/dolt/go/libraries/doltcore/conflict"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/store/hash"
@@ -60,17 +59,6 @@ type Table struct {
overriddenSchema schema.Schema
}
// NewNomsTable creates a noms Struct which stores row data, index data, and schema.
// Deprecated: use NewTable instead.
func NewNomsTable(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, sch schema.Schema, rows types.Map, indexes durable.IndexSet, autoIncVal types.Value) (*Table, error) {
dt, err := durable.NewNomsTable(ctx, vrw, ns, sch, rows, indexes, autoIncVal)
if err != nil {
return nil, err
}
return &Table{table: dt}, nil
}
// NewTable creates a durable object which stores row data, index data, and schema.
func NewTable(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, sch schema.Schema, rows durable.Index, indexes durable.IndexSet, autoIncVal types.Value) (*Table, error) {
dt, err := durable.NewTable(ctx, vrw, ns, sch, rows, indexes, autoIncVal)
@@ -80,11 +68,6 @@ func NewTable(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore,
return &Table{table: dt}, nil
}
// NewTableFromDurable creates a table from the given durable object.
func NewTableFromDurable(table durable.Table) *Table {
return &Table{table: table}
}
func NewEmptyTable(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, sch schema.Schema) (*Table, error) {
rows, err := durable.NewEmptyPrimaryIndex(ctx, vrw, ns, sch)
if err != nil {
@@ -127,24 +110,6 @@ func (t *Table) GetOverriddenSchema() schema.Schema {
return t.overriddenSchema
}
// SetConflicts sets the merge conflicts for this table.
func (t *Table) SetConflicts(ctx context.Context, schemas conflict.ConflictSchema, conflictData durable.ConflictIndex) (*Table, error) {
table, err := t.table.SetConflicts(ctx, schemas, conflictData)
if err != nil {
return nil, err
}
return &Table{table: table}, nil
}
// GetConflicts returns a map built from ValueReadWriter when there are no conflicts in table.
func (t *Table) GetConflicts(ctx context.Context) (conflict.ConflictSchema, durable.ConflictIndex, error) {
if t.Format() == types.Format_DOLT {
panic("should use artifacts")
}
return t.table.GetConflicts(ctx)
}
// HasConflicts returns true if this table contains merge conflicts.
func (t *Table) HasConflicts(ctx context.Context) (bool, error) {
if t.Format() == types.Format_DOLT {
@@ -155,7 +120,8 @@ func (t *Table) HasConflicts(ctx context.Context) (bool, error) {
return art.HasConflicts(ctx)
}
return t.table.HasConflicts(ctx)
panic("Unsupported format: " + t.Format().VersionString())
}
// GetArtifacts returns the merge artifacts for this table.
@@ -182,20 +148,7 @@ func (t *Table) NumRowsInConflict(ctx context.Context) (uint64, error) {
return artIdx.ConflictCount(ctx)
}
ok, err := t.table.HasConflicts(ctx)
if err != nil {
return 0, err
}
if !ok {
return 0, nil
}
_, cons, err := t.table.GetConflicts(ctx)
if err != nil {
return 0, err
}
return cons.Count(), nil
panic("Unsupported format: " + t.Format().VersionString())
}
// NumConstraintViolations returns the number of constraint violations for this table.
@@ -208,12 +161,7 @@ func (t *Table) NumConstraintViolations(ctx context.Context) (uint64, error) {
return artIdx.ConstraintViolationCount(ctx)
}
cvs, err := t.table.GetConstraintViolations(ctx)
if err != nil {
return 0, err
}
return cvs.Len(), nil
panic("Unsupported format: " + t.Format().VersionString())
}
// ClearConflicts deletes all merge conflicts for this table.
@@ -222,7 +170,7 @@ func (t *Table) ClearConflicts(ctx context.Context) (*Table, error) {
return t.clearArtifactConflicts(ctx)
}
return t.clearConflicts(ctx)
panic("Unsupported format: " + t.Format().VersionString())
}
func (t *Table) clearArtifactConflicts(ctx context.Context) (*Table, error) {
@@ -241,21 +189,13 @@ func (t *Table) clearArtifactConflicts(ctx context.Context) (*Table, error) {
return &Table{table: table}, nil
}
func (t *Table) clearConflicts(ctx context.Context) (*Table, error) {
table, err := t.table.ClearConflicts(ctx)
if err != nil {
return nil, err
}
return &Table{table: table}, nil
}
// GetConflictSchemas returns the merge conflict schemas for this table.
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)
}
return t.getNomsConflictSchemas(ctx)
panic("Unsupported format: " + t.Format().VersionString())
}
// The conflict schema is implicitly determined based on the first conflict in the artifacts table.
@@ -337,36 +277,6 @@ func tableFromRootIsh(ctx context.Context, vrw types.ValueReadWriter, ns tree.No
return tbl, ok, nil
}
func (t *Table) getNomsConflictSchemas(ctx context.Context) (base, sch, mergeSch schema.Schema, err error) {
cs, _, err := t.table.GetConflicts(ctx)
if err != nil {
return nil, nil, nil, err
}
return cs.Base, cs.Schema, cs.MergeSchema, nil
}
// GetConstraintViolations returns a map of all constraint violations for this table, along with a bool indicating
// whether the table has any violations.
func (t *Table) GetConstraintViolations(ctx context.Context) (types.Map, error) {
if t.Format() == types.Format_DOLT {
panic("should use artifacts")
}
return t.table.GetConstraintViolations(ctx)
}
// SetConstraintViolations sets this table's violations to the given map. If the map is empty, then the constraint
// violations entry on the embedded struct is removed.
func (t *Table) SetConstraintViolations(ctx context.Context, violationsMap types.Map) (*Table, error) {
if t.Format() == types.Format_DOLT {
panic("should use artifacts")
}
table, err := t.table.SetConstraintViolations(ctx, violationsMap)
if err != nil {
return nil, err
}
return &Table{table: table}, nil
}
// GetSchema returns the schema.Schema for this Table.
func (t *Table) GetSchema(ctx context.Context) (schema.Schema, error) {
return t.table.GetSchema(ctx)
@@ -406,18 +316,6 @@ func (t *Table) HashOf() (hash.Hash, error) {
return t.table.HashOf()
}
// UpdateNomsRows replaces the current row data and returns and updated Table.
// Calls to UpdateNomsRows will not be written to the database. The root must
// be updated with the updated table, and the root must be committed or written.
// Deprecated: use Table.UpdateRows() instead.
func (t *Table) UpdateNomsRows(ctx context.Context, updatedRows types.Map) (*Table, error) {
table, err := t.table.SetTableRows(ctx, durable.IndexFromNomsMap(updatedRows, t.ValueReadWriter(), t.NodeStore()))
if err != nil {
return nil, err
}
return &Table{table: table}, nil
}
// UpdateRows replaces the current row data and returns and updated Table.
// Calls to UpdateRows will not be written to the database. The root must
// be updated with the updated table, and the root must be committed or written.
@@ -429,17 +327,6 @@ func (t *Table) UpdateRows(ctx context.Context, updatedRows durable.Index) (*Tab
return &Table{table: table}, nil
}
// GetNomsRowData retrieves the underlying map which is a map from a primary key to a list of field values.
// Deprecated: use Table.GetRowData() instead.
func (t *Table) GetNomsRowData(ctx context.Context) (types.Map, error) {
idx, err := t.table.GetTableRows(ctx)
if err != nil {
return types.Map{}, err
}
return durable.NomsMapFromIndex(idx), nil
}
// GetRowData retrieves the underlying map which is a map from a primary key to a list of field values.
func (t *Table) GetRowData(ctx context.Context) (durable.Index, error) {
return t.table.GetTableRows(ctx)
@@ -458,57 +345,6 @@ func (t *Table) GetRowDataHash(ctx context.Context) (hash.Hash, error) {
return idx.HashOf()
}
// ResolveConflicts resolves conflicts for this table.
func (t *Table) ResolveConflicts(ctx context.Context, pkTuples []types.Value) (invalid, notFound []types.Value, tbl *Table, err error) {
removed := 0
conflictSchema, confIdx, err := t.GetConflicts(ctx)
if err != nil {
return nil, nil, nil, err
}
if confIdx.Format() == types.Format_DOLT {
panic("resolve conflicts not implemented for new storage format")
}
confData := durable.NomsMapFromConflictIndex(confIdx)
confEdit := confData.Edit()
for _, pkTupleVal := range pkTuples {
if has, err := confData.Has(ctx, pkTupleVal); err != nil {
return nil, nil, nil, err
} else if has {
removed++
confEdit.Remove(pkTupleVal)
} else {
notFound = append(notFound, pkTupleVal)
}
}
if removed == 0 {
return invalid, notFound, tbl, ErrNoConflictsResolved
}
conflicts, err := confEdit.Map(ctx)
if err != nil {
return nil, nil, nil, err
}
if conflicts.Len() == 0 {
table, err := t.table.ClearConflicts(ctx)
if err != nil {
return nil, nil, nil, err
}
return invalid, notFound, &Table{table: table}, nil
}
table, err := t.table.SetConflicts(ctx, conflictSchema, durable.ConflictIndexFromNomsMap(conflicts, t.ValueReadWriter()))
if err != nil {
return nil, nil, nil, err
}
return invalid, notFound, &Table{table: table}, nil
}
// GetIndexSet returns the internal index map which goes from index name to a ref of the row data map.
func (t *Table) GetIndexSet(ctx context.Context) (durable.IndexSet, error) {
return t.table.GetIndexes(ctx)
@@ -523,27 +359,6 @@ func (t *Table) SetIndexSet(ctx context.Context, indexes durable.IndexSet) (*Tab
return &Table{table: table}, nil
}
// GetNomsIndexRowData retrieves the underlying map of an index, in which the primary key consists of all indexed columns.
// Deprecated: use Table.GetIndexRowData() instead.
func (t *Table) GetNomsIndexRowData(ctx context.Context, indexName string) (types.Map, error) {
sch, err := t.GetSchema(ctx)
if err != nil {
return types.EmptyMap, err
}
indexes, err := t.GetIndexSet(ctx)
if err != nil {
return types.EmptyMap, err
}
idx, err := indexes.GetIndex(ctx, sch, nil, indexName)
if err != nil {
return types.EmptyMap, err
}
return durable.NomsMapFromIndex(idx), nil
}
// GetIndexRowData retrieves the underlying map of an index, in which the primary key consists of all indexed columns.
func (t *Table) GetIndexRowData(ctx context.Context, indexName string) (durable.Index, error) {
sch, err := t.GetSchema(ctx)
@@ -574,22 +389,6 @@ func (t *Table) SetIndexRows(ctx context.Context, indexName string, idx durable.
return t.SetIndexSet(ctx, indexes)
}
// SetNomsIndexRows replaces the current row data for the given index and returns an updated Table.
// Deprecated: use Table.SetIndexRows() instead.
func (t *Table) SetNomsIndexRows(ctx context.Context, indexName string, idx types.Map) (*Table, error) {
indexes, err := t.GetIndexSet(ctx)
if err != nil {
return nil, err
}
indexes, err = indexes.PutNomsIndex(ctx, indexName, idx)
if err != nil {
return nil, err
}
return t.SetIndexSet(ctx, indexes)
}
// DeleteIndexRowData removes the underlying map of an index, along with its key entry. This should only be used
// when removing an index altogether. If the intent is to clear an index's data, then use SetNomsIndexRows with
// an empty map.
@@ -1,322 +0,0 @@
// Copyright 2019 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 merge
import (
"context"
"errors"
"io"
"github.com/dolthub/dolt/go/libraries/doltcore/conflict"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/rowconv"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/store/types"
)
const (
oursStr = "our"
theirsStr = "their"
baseStr = "base"
)
const (
ConflictDiffTypeAdded = "added"
ConflictDiffTypeModified = "modified"
ConflictDiffTypeRemoved = "removed"
)
// ConflictReader is a class providing a NextConflict function which can be used in a pipeline as a pipeline.SourceFunc,
// or it can be used to read each conflict
type ConflictReader struct {
confItr types.MapIterator
joiner *rowconv.Joiner
sch schema.Schema
nbf *types.NomsBinFormat
keyless bool
}
// NewConflictReader returns a new conflict reader for a given table
func NewConflictReader(ctx context.Context, tbl *doltdb.Table, tblName doltdb.TableName) (*ConflictReader, error) {
base, sch, mergeSch, err := tbl.GetConflictSchemas(ctx, tblName) // tblName unused by old storage format
if err != nil {
return nil, err
}
if base == nil || sch == nil || mergeSch == nil {
base, err = tbl.GetSchema(ctx)
sch, mergeSch = base, base
}
if err != nil {
return nil, err
}
joiner, err := rowconv.NewJoiner(
[]rowconv.NamedSchema{
{Name: baseStr, Sch: base},
{Name: oursStr, Sch: sch},
{Name: theirsStr, Sch: mergeSch},
}, map[string]rowconv.ColNamingFunc{
baseStr: func(colName string) string { return baseStr + "_" + colName },
oursStr: func(colName string) string { return oursStr + "_" + colName },
theirsStr: func(colName string) string { return theirsStr + "_" + colName },
},
)
if err != nil {
return nil, err
}
readerSch := joiner.GetSchema()
readerSch, err = readerSch.AddColumn(schema.NewColumn("our_diff_type", schema.DoltConflictsOurDiffTypeTag, types.StringKind, false), nil)
if err != nil {
return nil, err
}
readerSch, err = readerSch.AddColumn(schema.NewColumn("their_diff_type", schema.DoltConflictsTheirDiffTypeTag, types.StringKind, false), nil)
if err != nil {
return nil, err
}
var keyless bool
if schema.IsKeyless(sch) {
keyless = true
readerSch, err = readerSch.AddColumn(
schema.NewColumn(
"base_cardinality",
schema.DoltConflictsBaseCardinalityTag,
types.UintKind,
false),
nil)
if err != nil {
return nil, err
}
readerSch, err = readerSch.AddColumn(
schema.NewColumn(
"our_cardinality",
schema.DoltConflictsOurCardinalityTag,
types.UintKind,
false),
nil)
if err != nil {
return nil, err
}
readerSch, err = readerSch.AddColumn(
schema.NewColumn(
"their_cardinality",
schema.DoltConflictsTheirCardinalityTag,
types.UintKind,
false),
nil)
if err != nil {
return nil, err
}
}
_, confIdx, err := tbl.GetConflicts(ctx)
if err != nil {
return nil, err
}
if confIdx.Format() == types.Format_DOLT {
panic("conflict reader not implemented for new storage format")
}
confData := durable.NomsMapFromConflictIndex(confIdx)
confItr, err := confData.Iterator(ctx)
if err != nil {
return nil, err
}
return &ConflictReader{
confItr: confItr,
joiner: joiner,
sch: readerSch,
nbf: tbl.Format(),
keyless: keyless,
}, nil
}
// GetSchema gets the schema of the rows that this reader will return
func (cr *ConflictReader) GetSchema() schema.Schema {
return cr.sch
}
// GetJoiner returns the joiner used to join a row with its base, and merge versions
func (cr *ConflictReader) GetJoiner() *rowconv.Joiner {
return cr.joiner
}
// NextConflict can be called successively to retrieve the conflicts in a table. Once all conflicts have been returned
// io.EOF will be returned in the error field. This can be used in a pipeline, or to iterate through all the conflicts
// in a table.
func (cr *ConflictReader) NextConflict(ctx context.Context) (row.Row, error) {
key, value, err := cr.confItr.Next(ctx)
if err != nil {
return nil, err
}
if key == nil {
return nil, io.EOF
}
keyTpl := key.(types.Tuple)
conflict, err := conflict.ConflictFromTuple(value.(types.Tuple))
if err != nil {
return nil, err
}
var joinedRow row.Row
if !cr.keyless {
joinedRow, err = cr.pkJoinedRow(keyTpl, conflict)
} else {
joinedRow, err = cr.keylessJoinedRow(keyTpl, conflict)
}
if err != nil {
return nil, err
}
ourDiffType := getDiffType(conflict.Base, conflict.Value)
theirDiffType := getDiffType(conflict.Base, conflict.MergeValue)
joinedRow, err = joinedRow.SetColVal(schema.DoltConflictsOurDiffTypeTag, types.String(ourDiffType), cr.sch)
if err != nil {
return nil, err
}
joinedRow, err = joinedRow.SetColVal(schema.DoltConflictsTheirDiffTypeTag, types.String(theirDiffType), cr.sch)
if err != nil {
return nil, err
}
return joinedRow, nil
}
func (cr *ConflictReader) pkJoinedRow(key types.Tuple, conflict conflict.Conflict) (row.Row, error) {
var err error
namedRows := make(map[string]row.Row)
if !types.IsNull(conflict.Base) {
namedRows[baseStr], err = row.FromNoms(cr.joiner.SchemaForName(baseStr), key, conflict.Base.(types.Tuple))
if err != nil {
return nil, err
}
}
if !types.IsNull(conflict.Value) {
namedRows[oursStr], err = row.FromNoms(cr.joiner.SchemaForName(oursStr), key, conflict.Value.(types.Tuple))
if err != nil {
return nil, err
}
}
if !types.IsNull(conflict.MergeValue) {
namedRows[theirsStr], err = row.FromNoms(cr.joiner.SchemaForName(theirsStr), key, conflict.MergeValue.(types.Tuple))
if err != nil {
return nil, err
}
}
joinedRow, err := cr.joiner.Join(namedRows)
if err != nil {
return nil, err
}
return joinedRow, nil
}
func (cr *ConflictReader) keylessJoinedRow(key types.Tuple, conflict conflict.Conflict) (row.Row, error) {
var err error
namedRows := make(map[string]row.Row)
var baseCard, ourCard, theirCard uint64
if !types.IsNull(conflict.Base) {
namedRows[baseStr], baseCard, err = row.KeylessRowsFromTuples(key, conflict.Base.(types.Tuple))
if err != nil {
return nil, err
}
}
if !types.IsNull(conflict.Value) {
namedRows[oursStr], ourCard, err = row.KeylessRowsFromTuples(key, conflict.Value.(types.Tuple))
if err != nil {
return nil, err
}
}
if !types.IsNull(conflict.MergeValue) {
namedRows[theirsStr], theirCard, err = row.KeylessRowsFromTuples(key, conflict.MergeValue.(types.Tuple))
if err != nil {
return nil, err
}
}
joinedRow, err := cr.joiner.Join(namedRows)
if err != nil {
return nil, err
}
joinedRow, err = setCardinalities(types.Uint(baseCard), types.Uint(ourCard), types.Uint(theirCard), joinedRow, cr.sch)
if err != nil {
return nil, err
}
return joinedRow, nil
}
func setCardinalities(base, ours, theirs types.Uint, joinedRow row.Row, sch schema.Schema) (row.Row, error) {
joinedRow, err := joinedRow.SetColVal(schema.DoltConflictsBaseCardinalityTag, base, sch)
if err != nil {
return nil, err
}
joinedRow, err = joinedRow.SetColVal(schema.DoltConflictsOurCardinalityTag, ours, sch)
if err != nil {
return nil, err
}
joinedRow, err = joinedRow.SetColVal(schema.DoltConflictsTheirCardinalityTag, theirs, sch)
if err != nil {
return nil, err
}
return joinedRow, nil
}
func getDiffType(base types.Value, other types.Value) string {
if types.IsNull(base) {
return ConflictDiffTypeAdded
} else if types.IsNull(other) {
return ConflictDiffTypeRemoved
}
return ConflictDiffTypeModified
}
// GetKeyForConflicts returns the pk for a conflict row
func (cr *ConflictReader) GetKeyForConflict(ctx context.Context, r row.Row) (types.Value, error) {
rows, err := cr.joiner.Split(r)
if err != nil {
return nil, err
}
for rowType, r := range rows {
key, err := r.NomsMapKey(cr.joiner.SchemaForName(rowType)).Value(ctx)
if err != nil {
return nil, err
}
return key, nil
}
return nil, errors.New("could not determine key")
}
// Close should release resources being held
func (cr *ConflictReader) Close() error {
return nil
}
@@ -325,11 +325,8 @@ func TestKeylessMergeConflicts(t *testing.T) {
}
func assertConflicts(t *testing.T, ctx context.Context, tbl *doltdb.Table, expected conflictEntries) {
if types.IsFormat_DOLT(tbl.Format()) {
assertProllyConflicts(t, ctx, tbl, expected)
return
}
assertNomsConflicts(t, ctx, tbl, expected)
types.AssertFormat_DOLT(tbl.Format())
assertProllyConflicts(t, ctx, tbl, expected)
}
func assertProllyConflicts(t *testing.T, ctx context.Context, tbl *doltdb.Table, expected conflictEntries) {
@@ -381,32 +378,6 @@ func assertProllyConflicts(t *testing.T, ctx context.Context, tbl *doltdb.Table,
}
func assertNomsConflicts(t *testing.T, ctx context.Context, tbl *doltdb.Table, expected conflictEntries) {
_, confIdx, err := tbl.GetConflicts(ctx)
require.NoError(t, err)
conflicts := durable.NomsMapFromConflictIndex(confIdx)
assert.True(t, conflicts.Len() > 0)
assert.Equal(t, int(conflicts.Len()), len(expected))
expectedSet := expected.toTupleSet()
actual, err := conflicts.Iterator(ctx)
require.NoError(t, err)
for {
_, act, err := actual.Next(ctx)
if act == nil {
return
}
assert.NoError(t, err)
h, err := act.Hash(types.Format_Default)
assert.NoError(t, err)
exp, ok := expectedSet[h]
assert.True(t, ok)
assert.True(t, exp.Equals(act))
}
}
func mustGetRowValueFromTable(t *testing.T, ctx context.Context, tbl *doltdb.Table, key val.Tuple) val.Tuple {
idx, err := tbl.GetRowData(ctx)
require.NoError(t, err)
@@ -434,12 +405,8 @@ func mustGetRowValueFromRootIsh(t *testing.T, ctx context.Context, vrw types.Val
// |expected| is a tupleSet to compensate for random storage order
func assertKeylessRows(t *testing.T, ctx context.Context, tbl *doltdb.Table, expected keylessEntries) {
if types.IsFormat_DOLT(tbl.Format()) {
assertKeylessProllyRows(t, ctx, tbl, expected)
return
}
assertKeylessNomsRows(t, ctx, tbl, expected)
types.AssertFormat_DOLT(tbl.Format())
assertKeylessProllyRows(t, ctx, tbl, expected)
}
func assertKeylessProllyRows(t *testing.T, ctx context.Context, tbl *doltdb.Table, expected []keylessEntry) {
@@ -470,30 +437,6 @@ func assertKeylessProllyRows(t *testing.T, ctx context.Context, tbl *doltdb.Tabl
require.Equal(t, len(expected), c)
}
func assertKeylessNomsRows(t *testing.T, ctx context.Context, tbl *doltdb.Table, expected keylessEntries) {
rowData, err := tbl.GetNomsRowData(ctx)
require.NoError(t, err)
assert.Equal(t, int(rowData.Len()), len(expected))
expectedSet := expected.toTupleSet()
actual, err := rowData.Iterator(ctx)
require.NoError(t, err)
for {
_, act, err := actual.Next(ctx)
if act == nil {
break
}
assert.NoError(t, err)
h, err := act.Hash(types.Format_Default)
assert.NoError(t, err)
exp, ok := expectedSet[h]
assert.True(t, ok)
assert.True(t, exp.Equals(act))
}
}
const tblName = "noKey"
var keylessSch = dtu.MustSchema(
+10 -96
View File
@@ -48,6 +48,12 @@ var ErrMultipleViolationsForRow = errors.New("multiple violations for row not su
var ErrSameTblAddedTwice = goerrors.NewKind("table with same name '%s' added in 2 commits can't be merged")
const (
ConflictDiffTypeAdded = "added"
ConflictDiffTypeModified = "modified"
ConflictDiffTypeRemoved = "removed"
)
func MergeCommits(ctx *sql.Context, tableResolver doltdb.TableResolver, commit, mergeCommit *doltdb.Commit, opts editor.Options) (*Result, error) {
optCmt, err := doltdb.GetCommitAncestor(ctx, commit, mergeCommit)
if err != nil {
@@ -169,23 +175,12 @@ func MergeRoots(
mergeOpts MergeOpts,
) (*Result, error) {
var (
conflictStash *conflictStash
violationStash *violationStash
nbf *types.NomsBinFormat
err error
nbf *types.NomsBinFormat
err error
)
nbf = ourRoot.VRW().Format()
if !types.IsFormat_DOLT(nbf) {
ourRoot, conflictStash, err = stashConflicts(ctx, ourRoot)
if err != nil {
return nil, err
}
ancRoot, violationStash, err = stashViolations(ctx, ancRoot)
if err != nil {
return nil, err
}
}
types.AssertFormat_DOLT(nbf)
// merge collations
oColl, err := ourRoot.GetCollation(ctx)
@@ -284,12 +279,7 @@ func MergeRoots(
continue
}
if mergedTable.conflict.Count() > 0 {
if types.IsFormat_DOLT(nbf) {
schConflicts = append(schConflicts, mergedTable.conflict)
} else {
// return schema conflict as error
return nil, mergedTable.conflict
}
schConflicts = append(schConflicts, mergedTable.conflict)
}
if mergedTable.table != nil {
@@ -387,39 +377,11 @@ func MergeRoots(
return nil, err
}
if types.IsFormat_DOLT(ourRoot.VRW().Format()) {
err = getConstraintViolationStats(ctx, mergedRoot, tblToStats)
if err != nil {
return nil, err
}
return &Result{
Root: mergedRoot,
SchemaConflicts: schConflicts,
Stats: tblToStats,
}, nil
}
mergedRoot, err = mergeCVsWithStash(ctx, mergedRoot, violationStash)
if err != nil {
return nil, err
}
err = getConstraintViolationStats(ctx, mergedRoot, tblToStats)
if err != nil {
return nil, err
}
mergedHasTableConflicts := checkForTableConflicts(tblToStats)
if !conflictStash.Empty() && mergedHasTableConflicts {
return nil, ErrCantOverwriteConflicts
} else if !conflictStash.Empty() {
mergedRoot, err = applyConflictStash(ctx, conflictStash.Stash, mergedRoot)
if err != nil {
return nil, err
}
}
return &Result{
Root: mergedRoot,
SchemaConflicts: schConflicts,
@@ -427,54 +389,6 @@ func MergeRoots(
}, nil
}
// mergeCVsWithStash merges the table constraint violations in |stash| with |root|.
// Returns an updated root with all the merged CVs.
func mergeCVsWithStash(ctx context.Context, root doltdb.RootValue, stash *violationStash) (doltdb.RootValue, error) {
updatedRoot := root
for name, stashed := range stash.Stash {
tbl, ok, err := root.GetTable(ctx, doltdb.TableName{Name: name})
if err != nil {
return nil, err
}
if !ok {
// the table with the CVs was deleted
continue
}
curr, err := tbl.GetConstraintViolations(ctx)
if err != nil {
return nil, err
}
unioned, err := types.UnionMaps(ctx, curr, stashed, func(key types.Value, currV types.Value, stashV types.Value) (types.Value, error) {
if !currV.Equals(stashV) {
panic(fmt.Sprintf("encountered conflict when merging constraint violations, conflicted key: %v\ncurrent value: %v\nstashed value: %v\n", key, currV, stashV))
}
return currV, nil
})
if err != nil {
return nil, err
}
tbl, err = tbl.SetConstraintViolations(ctx, unioned)
if err != nil {
return nil, err
}
updatedRoot, err = root.PutTable(ctx, doltdb.TableName{Name: name}, tbl)
if err != nil {
return nil, err
}
}
return updatedRoot, nil
}
// Checks if a table conflict occurred during the merge
func checkForTableConflicts(tblToStats map[doltdb.TableName]*MergeStats) bool {
for _, stat := range tblToStats {
if stat.HasConflicts() && !stat.HasRootObjectConflicts() {
return true
}
}
return false
}
// populates tblToStats with violation statistics
func getConstraintViolationStats(ctx context.Context, root doltdb.RootValue, tblToStats map[doltdb.TableName]*MergeStats) error {
for tblName, stats := range tblToStats {
+3 -32
View File
@@ -20,7 +20,6 @@ import (
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/libraries/doltcore/conflict"
"github.com/dolthub/dolt/go/libraries/doltcore/diff"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
@@ -213,6 +212,8 @@ func (rm *RootMerger) MergeTable(
return nil, nil, err
}
types.AssertFormat_DOLT(tm.vrw.Format())
// short-circuit here if we can
finished, finishedRootObj, stats, err := rm.MaybeShortCircuit(ctx, tm, mergeOpts)
if finished != nil || finishedRootObj != nil || stats != nil || err != nil {
@@ -243,11 +244,7 @@ func (rm *RootMerger) MergeTable(
var tbl *doltdb.Table
var rootObj doltdb.RootObject
if !tm.InvolvesRootObjects() {
if types.IsFormat_DOLT(tm.vrw.Format()) {
tbl, stats, err = mergeProllyTable(ctx, tm, mergeSch, mergeInfo, diffInfo)
} else {
panic("data format not supported in this version of Dolt")
}
tbl, stats, err = mergeProllyTable(ctx, tm, mergeSch, mergeInfo, diffInfo)
if err != nil {
return nil, nil, err
}
@@ -501,32 +498,6 @@ var MergeRootObjects = func(ctx context.Context, mro MergeRootObject) (doltdb.Ro
return nil, nil, errors.New("Dolt does not operate on root objects")
}
func setConflicts(ctx context.Context, cons durable.ConflictIndex, tbl, mergeTbl, ancTbl, tableToUpdate *doltdb.Table) (*doltdb.Table, error) {
ancSch, err := ancTbl.GetSchema(ctx)
if err != nil {
return nil, err
}
sch, err := tbl.GetSchema(ctx)
if err != nil {
return nil, err
}
mergeSch, err := mergeTbl.GetSchema(ctx)
if err != nil {
return nil, err
}
cs := conflict.NewConflictSchema(ancSch, sch, mergeSch)
tableToUpdate, err = tableToUpdate.SetConflicts(ctx, cs, cons)
if err != nil {
return nil, err
}
return tableToUpdate, nil
}
func calcTableMergeStats(ctx context.Context, tbl *doltdb.Table, mergeTbl *doltdb.Table) (MergeStats, error) {
ms := MergeStats{Operation: TableModified}
+2 -2
View File
@@ -187,10 +187,10 @@ func SchemaMerge(
// TODO: We'll remove this once it's possible to get diff and merge on different primary key sets
// TODO: decide how to merge different orders of PKS
if !schema.ArePrimaryKeySetsDiffable(format, ourSch, theirSch) {
if !schema.ArePrimaryKeySetsDiffable(ourSch, theirSch) {
return nil, SchemaConflict{}, mergeInfo, diffInfo, ErrMergeWithDifferentPks.New(tblName)
}
if !schema.ArePrimaryKeySetsDiffable(format, ourSch, ancSch) {
if !schema.ArePrimaryKeySetsDiffable(ourSch, ancSch) {
return nil, SchemaConflict{}, mergeInfo, diffInfo, ErrMergeWithDifferentPksFromAncestor.New(tblName)
}
@@ -297,10 +297,6 @@ var testRows = []testRow{
}
func TestMergeCommits(t *testing.T) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip()
}
ddb, vrw, ns, rightCommitHash, ancCommitHash, root, mergeRoot, ancRoot, expectedRows, expectedArtifacts := setupMergeTest(t)
defer ddb.Close()
merger, err := NewMerger(root, mergeRoot, ancRoot, rightCommitHash, ancCommitHash, vrw, ns)
@@ -40,7 +40,6 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/table/editor"
"github.com/dolthub/dolt/go/store/hash"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/types"
)
type schemaMergeTest struct {
@@ -1654,7 +1653,7 @@ func testSchemaMergeHelper(t *testing.T, tests []schemaMergeTest, flipSides bool
eo = eo.WithDeaf(editor.NewInMemDeaf(a.VRW()))
// attempt merge before skipping to assert no panics
result, err := merge.MergeRoots(sql.NewContext(ctx), doltdb.SimpleTableResolver{}, l, r, a, rootish{r}, rootish{a}, eo, mo)
maybeSkip(t, a.VRW().Format(), test, flipSides)
maybeSkip(t, test, flipSides)
if test.conflict {
// TODO: Test the conflict error message more deeply
@@ -1829,15 +1828,9 @@ func verifyMerge(t *testing.T, ctx context.Context, m doltdb.RootValue, result *
}
}
func maybeSkip(t *testing.T, nbf *types.NomsBinFormat, test schemaMergeTest, flipSides bool) {
if types.IsFormat_DOLT(nbf) {
if test.skipNewFmt || flipSides && test.skipFlipOnNewFormat {
t.Skip()
}
} else {
if test.skipOldFmt || flipSides && test.skipFlipOnOldFormat {
t.Skip()
}
func maybeSkip(t *testing.T, test schemaMergeTest, flipSides bool) {
if test.skipNewFmt || flipSides && test.skipFlipOnNewFormat {
t.Skip()
}
}
-182
View File
@@ -1,182 +0,0 @@
// Copyright 2022 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 merge
import (
"context"
"github.com/dolthub/dolt/go/libraries/doltcore/conflict"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/store/types"
)
type conflictStash struct {
Stash map[string]*conflictData
}
type conflictData struct {
HasConflicts bool
Sch conflict.ConflictSchema
ConfIdx durable.ConflictIndex
}
// Empty returns false if any table has a conflict.
// True otherwise.
func (s *conflictStash) Empty() bool {
for _, data := range s.Stash {
if data.HasConflicts {
return false
}
}
return true
}
type violationStash struct {
// todo: durable
Stash map[string]types.Map
}
// Empty returns false if any table has constraint violations.
// True otherwise.
func (s *violationStash) Empty() bool {
for _, data := range s.Stash {
if data.Len() > 0 {
return false
}
}
return true
}
func stashConflicts(ctx context.Context, root doltdb.RootValue) (doltdb.RootValue, *conflictStash, error) {
names, err := root.GetTableNames(ctx, doltdb.DefaultSchemaName, true)
if err != nil {
return nil, nil, err
}
updatedRoot := root
stash := make(map[string]*conflictData, len(names))
for _, name := range names {
tbl, _, err := root.GetTable(ctx, doltdb.TableName{Name: name})
if err != nil {
return nil, nil, err
}
d, err := getConflictData(ctx, tbl)
if err != nil {
return nil, nil, err
}
stash[name] = d
tbl, err = tbl.ClearConflicts(ctx)
if err != nil {
return nil, nil, err
}
updatedRoot, err = updatedRoot.PutTable(ctx, doltdb.TableName{Name: name}, tbl)
if err != nil {
return nil, nil, err
}
}
return updatedRoot, &conflictStash{stash}, nil
}
func stashViolations(ctx context.Context, root doltdb.RootValue) (doltdb.RootValue, *violationStash, error) {
names, err := root.GetTableNames(ctx, doltdb.DefaultSchemaName, true)
if err != nil {
return nil, nil, err
}
updatedRoot := root
stash := make(map[string]types.Map, len(names))
for _, name := range names {
tbl, _, err := root.GetTable(ctx, doltdb.TableName{Name: name})
if err != nil {
return nil, nil, err
}
v, err := tbl.GetConstraintViolations(ctx)
stash[name] = v
tbl, err = tbl.SetConstraintViolations(ctx, types.EmptyMap)
if err != nil {
return nil, nil, err
}
updatedRoot, err = updatedRoot.PutTable(ctx, doltdb.TableName{Name: name}, tbl)
if err != nil {
return nil, nil, err
}
}
return updatedRoot, &violationStash{stash}, nil
}
// applyConflictStash applies the data in |stash| to the root value. Missing
// tables will be skipped. This function will override any previous conflict
// data.
func applyConflictStash(ctx context.Context, stash map[string]*conflictData, root doltdb.RootValue) (doltdb.RootValue, error) {
updatedRoot := root
for name, data := range stash {
tbl, ok, err := root.GetTable(ctx, doltdb.TableName{Name: name})
if err != nil {
return nil, err
}
if !ok {
continue
}
tbl, err = setConflictData(ctx, tbl, data)
if err != nil {
return nil, err
}
updatedRoot, err = updatedRoot.PutTable(ctx, doltdb.TableName{Name: name}, tbl)
if err != nil {
return nil, err
}
}
return updatedRoot, nil
}
func getConflictData(ctx context.Context, tbl *doltdb.Table) (*conflictData, error) {
var sch conflict.ConflictSchema
var confIdx durable.ConflictIndex
hasCnf, err := tbl.HasConflicts(ctx)
if err != nil {
return nil, err
}
if hasCnf {
sch, confIdx, err = tbl.GetConflicts(ctx)
if err != nil {
return nil, err
}
}
return &conflictData{
HasConflicts: hasCnf,
Sch: sch,
ConfIdx: confIdx,
}, nil
}
func setConflictData(ctx context.Context, tbl *doltdb.Table, data *conflictData) (*doltdb.Table, error) {
var err error
if !data.HasConflicts {
tbl, err = tbl.ClearConflicts(ctx)
} else {
tbl, err = tbl.SetConflicts(ctx, data.Sch, data.ConfIdx)
}
if err != nil {
return nil, err
}
return tbl, nil
}
+18 -313
View File
@@ -18,21 +18,14 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"time"
"github.com/dolthub/go-mysql-server/sql"
gmstypes "github.com/dolthub/go-mysql-server/sql/types"
"github.com/dolthub/dolt/go/libraries/doltcore/diff"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
json2 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/json"
"github.com/dolthub/dolt/go/libraries/doltcore/table"
"github.com/dolthub/dolt/go/libraries/doltcore/table/typed/noms"
diff2 "github.com/dolthub/dolt/go/store/diff"
"github.com/dolthub/dolt/go/store/hash"
"github.com/dolthub/dolt/go/store/prolly"
"github.com/dolthub/dolt/go/store/types"
@@ -118,13 +111,13 @@ func RegisterForeignKeyViolations(
if err != nil {
return err
}
err = parentFkConstraintViolations(ctx, baseRoot.VRW(), foreignKey, postParent, postParent, postChild, emptyIdx, receiver)
err = parentFkConstraintViolations(ctx, foreignKey, postParent, postParent, postChild, emptyIdx, receiver)
if err != nil {
return err
}
} else {
// Parent exists in the ancestor
err = parentFkConstraintViolations(ctx, baseRoot.VRW(), foreignKey, preParent, postParent, postChild, preParent.RowData, receiver)
err = parentFkConstraintViolations(ctx, foreignKey, preParent, postParent, postChild, preParent.RowData, receiver)
if err != nil {
return err
}
@@ -266,54 +259,28 @@ func (f *foreignKeyViolationWriter) StartFK(ctx *sql.Context, fk doltdb.ForeignK
return err
}
if types.IsFormat_DOLT(tbl.Format()) {
arts, err := tbl.GetArtifacts(ctx)
if err != nil {
return err
}
artMap := durable.ProllyMapFromArtifactIndex(arts)
f.artEditor = artMap.Editor()
f.cInfoJsonData = jsonData
f.kd = sch.GetKeyDescriptor(tbl.NodeStore())
} else {
violMap, err := tbl.GetConstraintViolations(ctx)
if err != nil {
return err
}
f.violMapEditor = violMap.Edit()
f.nomsVInfo, err = jsonDataToNomsValue(ctx, tbl.ValueReadWriter(), jsonData)
if err != nil {
return err
}
types.AssertFormat_DOLT(tbl.Format())
arts, err := tbl.GetArtifacts(ctx)
if err != nil {
return err
}
artMap := durable.ProllyMapFromArtifactIndex(arts)
f.artEditor = artMap.Editor()
f.cInfoJsonData = jsonData
f.kd = sch.GetKeyDescriptor(tbl.NodeStore())
return nil
}
func (f *foreignKeyViolationWriter) EndCurrFK(ctx context.Context) error {
if types.IsFormat_DOLT(f.currTbl.Format()) {
artMap, err := f.artEditor.Flush(ctx)
if err != nil {
return err
}
artIdx := durable.ArtifactIndexFromProllyMap(artMap)
tbl, err := f.currTbl.SetArtifacts(ctx, artIdx)
if err != nil {
return err
}
f.rootValue, err = f.rootValue.PutTable(ctx, f.currFk.TableName, tbl)
if err != nil {
return err
}
return nil
}
types.AssertFormat_DOLT(f.currTbl.Format())
violMap, err := f.violMapEditor.Map(ctx)
artMap, err := f.artEditor.Flush(ctx)
if err != nil {
return err
}
tbl, err := f.currTbl.SetConstraintViolations(ctx, violMap)
artIdx := durable.ArtifactIndexFromProllyMap(artMap)
tbl, err := f.currTbl.SetArtifacts(ctx, artIdx)
if err != nil {
return err
}
@@ -325,21 +292,18 @@ func (f *foreignKeyViolationWriter) EndCurrFK(ctx context.Context) error {
}
func (f *foreignKeyViolationWriter) NomsFKViolationFound(ctx context.Context, rowKey, rowValue types.Tuple) error {
cvKey, cvVal, err := toConstraintViolationRow(ctx, CvType_ForeignKey, f.nomsVInfo, rowKey, rowValue)
if err != nil {
return err
}
f.violMapEditor.Set(cvKey, cvVal)
f.violatedTables.Add(f.currFk.TableName)
return nil
}
func (f *foreignKeyViolationWriter) ProllyFKViolationFound(ctx context.Context, rowKey, rowValue val.Tuple) error {
meta := prolly.ConstraintViolationMeta{VInfo: f.cInfoJsonData, Value: rowValue}
err := f.artEditor.ReplaceConstraintViolation(ctx, rowKey, f.theirRootIsh, prolly.ArtifactTypeForeignKeyViol, meta)
@@ -357,16 +321,15 @@ var _ FKViolationReceiver = (*foreignKeyViolationWriter)(nil)
// parentFkConstraintViolations processes foreign key constraint violations for the parent in a foreign key.
func parentFkConstraintViolations(
ctx context.Context,
vr types.ValueReader,
foreignKey doltdb.ForeignKey,
preParent, postParent, postChild *constraintViolationsLoadedTable,
preParentRowData durable.Index,
receiver FKViolationReceiver,
) error {
if preParentRowData.Format() != types.Format_DOLT {
m := durable.NomsMapFromIndex(preParentRowData)
return nomsParentFkConstraintViolations(ctx, vr, foreignKey, postParent, postChild, preParent.Schema, m, receiver)
panic("unsupported format: " + preParentRowData.Format().VersionString())
}
if preParent.IndexData == nil || postParent.Schema.GetPKCols().Size() == 0 || preParent.Schema.GetPKCols().Size() == 0 {
m, err := durable.ProllyMapFromIndex(preParentRowData)
if err != nil {
@@ -405,9 +368,9 @@ func childFkConstraintViolations(
receiver FKViolationReceiver,
) error {
if preChildRowData.Format() != types.Format_DOLT {
m := durable.NomsMapFromIndex(preChildRowData)
return nomsChildFkConstraintViolations(ctx, vr, foreignKey, postParent, postChild, preChild.Schema, m, receiver)
panic("unsupported format: " + preChildRowData.Format().VersionString())
}
if preChild.IndexData == nil || postChild.Schema.GetPKCols().Size() == 0 || preChild.Schema.GetPKCols().Size() == 0 {
m, err := durable.ProllyMapFromIndex(preChildRowData)
if err != nil {
@@ -436,264 +399,6 @@ func childFkConstraintViolations(
return prollyChildSecDiffFkConstraintViolations(ctx, foreignKey, postParent, postChild, m, receiver)
}
func nomsParentFkConstraintViolations(
ctx context.Context,
vr types.ValueReader,
foreignKey doltdb.ForeignKey,
postParent, postChild *constraintViolationsLoadedTable,
preParentSch schema.Schema,
preParentRowData types.Map,
receiver FKViolationReceiver) error {
postParentIndexTags := postParent.Index.IndexedColumnTags()
postChildIndexTags := postChild.Index.IndexedColumnTags()
differ := diff.NewRowDiffer(ctx, preParentRowData.Format(), preParentSch, postParent.Schema, 1024)
defer differ.Close()
differ.Start(ctx, preParentRowData, durable.NomsMapFromIndex(postParent.RowData))
for {
diffSlice, hasMore, err := differ.GetDiffs(1, 10*time.Second)
if err != nil {
return err
}
if len(diffSlice) != 1 {
if hasMore {
return fmt.Errorf("no diff returned but should have errored earlier")
}
break
}
rowDiff := diffSlice[0]
switch rowDiff.ChangeType {
case types.DiffChangeRemoved, types.DiffChangeModified:
postParentRow, err := row.FromNoms(postParent.Schema, rowDiff.KeyValue.(types.Tuple), rowDiff.OldValue.(types.Tuple))
if err != nil {
return err
}
hasNulls := false
for _, tag := range postParentIndexTags {
if postParentRowEntry, ok := postParentRow.GetColVal(tag); !ok || types.IsNull(postParentRowEntry) {
hasNulls = true
break
}
}
if hasNulls {
continue
}
postParentIndexPartialKey, err := row.ReduceToIndexPartialKey(foreignKey.TableColumns, postParent.Index, postParentRow)
if err != nil {
return err
}
shouldContinue, err := func() (bool, error) {
var mapIter table.ReadCloser = noms.NewNomsRangeReader(
vr,
postParent.IndexSchema,
durable.NomsMapFromIndex(postParent.IndexData),
[]*noms.ReadRange{{Start: postParentIndexPartialKey, Inclusive: true, Reverse: false, Check: noms.InRangeCheckPartial(postParentIndexPartialKey)}})
defer mapIter.Close(ctx)
if _, err := mapIter.ReadRow(ctx); err == nil {
// If the parent table has other rows that satisfy the partial key then we choose to do nothing
return true, nil
} else if err != io.EOF {
return false, err
}
return false, nil
}()
if err != nil {
return err
}
if shouldContinue {
continue
}
postParentIndexPartialKeySlice, err := postParentIndexPartialKey.AsSlice()
if err != nil {
return err
}
for i := 0; i < len(postChildIndexTags); i++ {
postParentIndexPartialKeySlice[2*i] = types.Uint(postChildIndexTags[i])
}
postChildIndexPartialKey, err := types.NewTuple(postChild.Table.Format(), postParentIndexPartialKeySlice...)
if err != nil {
return err
}
err = nomsParentFkConstraintViolationsProcess(ctx, vr, foreignKey, postChild, postChildIndexPartialKey, receiver)
if err != nil {
return err
}
case types.DiffChangeAdded:
// We don't do anything if a parent row was added
default:
return fmt.Errorf("unknown diff change type")
}
if !hasMore {
break
}
}
return nil
}
func nomsParentFkConstraintViolationsProcess(
ctx context.Context,
vr types.ValueReader,
foreignKey doltdb.ForeignKey,
postChild *constraintViolationsLoadedTable,
postChildIndexPartialKey types.Tuple,
receiver FKViolationReceiver,
) error {
indexData := durable.NomsMapFromIndex(postChild.IndexData)
rowData := durable.NomsMapFromIndex(postChild.RowData)
mapIter := noms.NewNomsRangeReader(
vr,
postChild.IndexSchema,
indexData,
[]*noms.ReadRange{{Start: postChildIndexPartialKey, Inclusive: true, Reverse: false, Check: noms.InRangeCheckPartial(postChildIndexPartialKey)}})
defer mapIter.Close(ctx)
var postChildIndexRow row.Row
var err error
for postChildIndexRow, err = mapIter.ReadRow(ctx); err == nil; postChildIndexRow, err = mapIter.ReadRow(ctx) {
postChildIndexKey, err := postChildIndexRow.NomsMapKey(postChild.IndexSchema).Value(ctx)
if err != nil {
return err
}
postChildRowKey, err := postChild.Index.ToTableTuple(ctx, postChildIndexKey.(types.Tuple), postChild.Table.Format())
if err != nil {
return err
}
postChildRowVal, ok, err := rowData.MaybeGetTuple(ctx, postChildRowKey)
if err != nil {
return err
}
if !ok {
return fmt.Errorf("index %s on %s contains data that table does not", foreignKey.TableIndex, foreignKey.TableName)
}
err = receiver.NomsFKViolationFound(ctx, postChildRowKey, postChildRowVal)
if err != nil {
return err
}
}
if err != io.EOF {
return err
}
return nil
}
// nomsChildFkConstraintViolations processes foreign key constraint violations for the child in a foreign key.
func nomsChildFkConstraintViolations(
ctx context.Context,
vr types.ValueReader,
foreignKey doltdb.ForeignKey,
postParent, postChild *constraintViolationsLoadedTable,
preChildSch schema.Schema,
preChildRowData types.Map,
receiver FKViolationReceiver,
) error {
var postParentIndexTags, postChildIndexTags []uint64
if postParent.Index.Name() == "" {
postParentIndexTags = foreignKey.ReferencedTableColumns
postChildIndexTags = foreignKey.TableColumns
} else {
postParentIndexTags = postParent.Index.IndexedColumnTags()
postChildIndexTags = postChild.Index.IndexedColumnTags()
}
differ := diff.NewRowDiffer(ctx, preChildRowData.Format(), preChildSch, postChild.Schema, 1024)
defer differ.Close()
differ.Start(ctx, preChildRowData, durable.NomsMapFromIndex(postChild.RowData))
for {
diffSlice, hasMore, err := differ.GetDiffs(1, 10*time.Second)
if err != nil {
return err
}
if len(diffSlice) != 1 {
if hasMore {
return fmt.Errorf("no diff returned but should have errored earlier")
}
break
}
rowDiff := diffSlice[0]
switch rowDiff.ChangeType {
case types.DiffChangeAdded, types.DiffChangeModified:
postChildRow, err := row.FromNoms(postChild.Schema, rowDiff.KeyValue.(types.Tuple), rowDiff.NewValue.(types.Tuple))
if err != nil {
return err
}
hasNulls := false
for _, tag := range postChildIndexTags {
if postChildRowEntry, ok := postChildRow.GetColVal(tag); !ok || types.IsNull(postChildRowEntry) {
hasNulls = true
break
}
}
if hasNulls {
continue
}
postChildIndexPartialKey, err := row.ReduceToIndexPartialKey(postChildIndexTags, postChild.Index, postChildRow)
if err != nil {
return err
}
postChildIndexPartialKeySlice, err := postChildIndexPartialKey.AsSlice()
if err != nil {
return err
}
for i := 0; i < len(postParentIndexTags); i++ {
postChildIndexPartialKeySlice[2*i] = types.Uint(postParentIndexTags[i])
}
parentPartialKey, err := types.NewTuple(postChild.Table.Format(), postChildIndexPartialKeySlice...)
if err != nil {
return err
}
err = childFkConstraintViolationsProcess(ctx, vr, postParent, rowDiff, parentPartialKey, receiver)
if err != nil {
return err
}
case types.DiffChangeRemoved:
// We don't do anything if a child row was removed
default:
return fmt.Errorf("unknown diff change type")
}
if !hasMore {
break
}
}
return nil
}
// childFkConstraintViolationsProcess handles processing the constraint violations for the child of a foreign key.
func childFkConstraintViolationsProcess(
ctx context.Context,
vr types.ValueReader,
postParent *constraintViolationsLoadedTable,
rowDiff *diff2.Difference,
parentPartialKey types.Tuple,
receiver FKViolationReceiver,
) error {
var mapIter table.ReadCloser = noms.NewNomsRangeReader(
vr,
postParent.IndexSchema,
durable.NomsMapFromIndex(postParent.IndexData),
[]*noms.ReadRange{{Start: parentPartialKey, Inclusive: true, Reverse: false, Check: noms.InRangeCheckPartial(parentPartialKey)}})
defer mapIter.Close(ctx)
// If the row exists in the parent, then we don't need to do anything
if _, err := mapIter.ReadRow(ctx); err != nil {
if err != io.EOF {
return err
}
err = receiver.NomsFKViolationFound(ctx, rowDiff.KeyValue.(types.Tuple), rowDiff.NewValue.(types.Tuple))
if err != nil {
return err
}
return nil
}
return nil
}
// newConstraintViolationsLoadedTable returns a *constraintViolationsLoadedTable. Returns false if the table was loaded
// but the index could not be found. If the table could not be found, then an error is returned.
func newConstraintViolationsLoadedTable(
@@ -1,265 +0,0 @@
// Copyright 2022 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 migrate
import (
"context"
"fmt"
"path/filepath"
"strings"
"time"
"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/utils/earl"
"github.com/dolthub/dolt/go/libraries/utils/filesys"
"github.com/dolthub/dolt/go/store/datas"
"github.com/dolthub/dolt/go/store/types"
)
const (
doltDir = dbfactory.DoltDir
nomsDir = dbfactory.DataDir
oldGenDir = "oldgen"
manifestFile = "manifest"
migrationRef = "migration"
)
var (
targetFormat = types.Format_DOLT
migrationMsg = fmt.Sprintf("migrating database to Noms Binary Format %s", targetFormat.VersionString())
)
// Environment is a migration environment.
type Environment struct {
Migration *env.DoltEnv
Existing *env.DoltEnv
DropConflicts bool
}
// NewEnvironment creates a migration Environment for |existing|.
func NewEnvironment(ctx context.Context, existing *env.DoltEnv) (Environment, error) {
mfs, err := getMigrateFS(existing.FS)
if err != nil {
return Environment{}, err
}
if err = initMigrationDB(ctx, existing, existing.FS, mfs); err != nil {
return Environment{}, err
}
mdb, err := doltdb.LoadDoltDB(ctx, targetFormat, doltdb.LocalDirDoltDB, mfs)
if err != nil {
return Environment{}, err
}
config, err := env.LoadDoltCliConfig(env.GetCurrentUserHomeDir, mfs)
if err != nil {
return Environment{}, err
}
migration := env.NewDoltEnv(
existing.Version,
config,
existing.RepoState,
mdb,
mfs,
)
return Environment{
Migration: migration,
Existing: existing,
}, nil
}
func initMigrationDB(ctx context.Context, existing *env.DoltEnv, src, dest filesys.Filesys) (err error) {
base, err := src.Abs(".")
if err != nil {
return err
}
ierr := src.Iter(doltDir, true, func(path string, size int64, isDir bool) (stop bool) {
path, err = filepath.Rel(base, path)
if err != nil {
stop = true
return
}
if isDir {
err = dest.MkDirs(path)
stop = err != nil
return
}
if strings.Contains(path, nomsDir) {
return
}
if err = filesys.CopyFile(path, path, src, dest); err != nil {
stop = true
return
}
return
})
if ierr != nil {
return ierr
}
if err != nil {
return err
}
absPath, err := dest.Abs(filepath.Join(doltDir, nomsDir))
if err != nil {
return err
}
if err = dest.MkDirs(absPath); err != nil {
return err
}
u, err := earl.Parse("file://" + filepath.ToSlash(absPath))
if err != nil {
return err
}
params := map[string]any{dbfactory.ChunkJournalParam: struct{}{}}
ddb, err := doltdb.LoadDoltDBWithParams(ctx, targetFormat, u.String(), dest, params)
vrw := ddb.ValueReadWriter()
ns := ddb.NodeStore()
db := doltdb.HackDatasDatabaseFromDoltDB(ddb)
// write init commit for migration
name, email, err := env.GetNameAndEmail(existing.Config)
if err != nil {
return err
}
meta, err := datas.NewCommitMeta(name, email, migrationMsg)
if err != nil {
return err
}
rv, err := doltdb.EmptyRootValue(ctx, vrw, ns)
if err != nil {
return err
}
ds, err := db.GetDataset(ctx, ref.NewInternalRef(migrationRef).String())
if err != nil {
return err
}
_, err = db.Commit(ctx, ds, rv.NomsValue(), datas.CommitOptions{Meta: meta})
return nil
}
// SwapChunkStores atomically swaps the ChunkStores of |menv.Migration| and |menv.Existing|.
func SwapChunkStores(ctx context.Context, menv Environment) error {
src, dest := menv.Migration.FS, menv.Existing.FS
absSrc, err := src.Abs(filepath.Join(doltDir, nomsDir))
if err != nil {
return err
}
absDest, err := dest.Abs(filepath.Join(doltDir, nomsDir))
if err != nil {
return err
}
var cpErr error
err = src.Iter(absSrc, true, func(p string, size int64, isDir bool) (stop bool) {
if strings.Contains(p, manifestFile) || isDir {
return
}
var relPath string
if relPath, cpErr = filepath.Rel(absSrc, p); cpErr != nil {
stop = true
return
}
srcPath := filepath.Join(absSrc, relPath)
destPath := filepath.Join(absDest, relPath)
if cpErr = filesys.CopyFile(srcPath, destPath, src, dest); cpErr != nil {
stop = true
}
return
})
if err != nil {
return err
}
if cpErr != nil {
return cpErr
}
return swapManifests(ctx, src, dest)
}
func swapManifests(ctx context.Context, src, dest filesys.Filesys) (err error) {
// backup the current manifest
manifest := filepath.Join(doltDir, nomsDir, manifestFile)
bak := filepath.Join(doltDir, nomsDir, manifestFile+".bak")
if err = filesys.CopyFile(manifest, bak, dest, dest); err != nil {
return err
}
// backup the current oldgen manifest, if one exists
gcManifest := filepath.Join(doltDir, nomsDir, oldGenDir, manifestFile)
oldGen, _ := dest.Exists(gcManifest)
if oldGen {
bak = filepath.Join(doltDir, nomsDir, oldGenDir, manifestFile+".bak")
if err = filesys.CopyFile(gcManifest, bak, dest, dest); err != nil {
return err
}
}
// copy manifest to |dest| under temporary name
tmp := filepath.Join(doltDir, nomsDir, "temp-manifest")
if err = filesys.CopyFile(manifest, tmp, src, dest); err != nil {
return err
}
// delete current oldgen manifest
if oldGen {
if err = dest.Delete(gcManifest, true); err != nil {
return err
}
}
// atomically swap the manifests
return dest.MoveFile(tmp, manifest)
// exit immediately!
}
func getMigrateFS(existing filesys.Filesys) (filesys.Filesys, error) {
uniq := fmt.Sprintf("dolt_migration_%d", time.Now().UnixNano())
tmpPath := filepath.Join(existing.TempDir(), uniq)
if err := existing.MkDirs(tmpPath); err != nil {
return nil, err
}
mfs, err := filesys.LocalFilesysWithWorkingDir(tmpPath)
if err != nil {
return nil, err
}
if err = mfs.MkDirs(doltDir); err != nil {
return nil, err
}
return mfs, nil
}
@@ -1,263 +0,0 @@
// Copyright 2022 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 migrate_test
import (
"context"
"testing"
"github.com/dolthub/go-mysql-server/sql"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dolthub/dolt/go/cmd/dolt/commands"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/migrate"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle"
"github.com/dolthub/dolt/go/store/chunks"
"github.com/dolthub/dolt/go/store/datas"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/types"
)
type migrationTest struct {
name string
hook setupHook
setup []string
asserts []assertion
err string
}
// a setupHook performs special setup operations that cannot
// be performed via SQL statements (eg TEXT type PRIMARY KEY)
type setupHook func(context.Context, *env.DoltEnv) (*env.DoltEnv, error)
type assertion struct {
query string
expected []sql.Row
}
func TestMigration(t *testing.T) {
if types.Format_Default != types.Format_LD_1 {
t.Skip()
}
tests := []migrationTest{
{
name: "smoke test",
setup: []string{
"CREATE TABLE test (pk int primary key)",
"INSERT INTO test VALUES (1),(2),(3)",
"CALL dolt_add('.')",
"CALL dolt_commit('-am', 'new table')",
},
asserts: []assertion{
{
query: "SELECT * FROM test",
expected: []sql.Row{{int32(1)}, {int32(2)}, {int32(3)}},
},
{
query: "SELECT count(*) FROM dolt_log",
expected: []sql.Row{{int64(2)}},
},
{
query: "SELECT count(*) FROM `dolt/dolt_migrated_commits`.dolt_commit_mapping",
expected: []sql.Row{{int64(2)}},
},
},
},
{
name: "TEXT primary key, BLOB secondary key",
hook: SetupHookRefKeys,
setup: []string{
// from setup hook:
// CREATE TABLE test (
// pk TEXT PRIMARY KEY,
// c0 int,
// c1 BLOB,
// INDEX blob_idx(c1),
// );
"INSERT INTO test VALUES ('a', 2, 'a')",
"CALL dolt_add('.')",
"CALL dolt_commit('-am', 'new table')",
},
asserts: []assertion{
{
query: "SELECT * FROM test",
expected: []sql.Row{{"a", int32(2), []byte("a")}},
},
{
query: "DESCRIBE test",
expected: []sql.Row{
{"pk", "varchar(16383)", "NO", "PRI", "NULL", ""},
{"c0", "int", "YES", "", "NULL", ""},
{"c1", "varbinary(16383)", "YES", "MUL", "NULL", ""},
},
},
},
},
{
name: "create more commits",
setup: []string{
"CREATE TABLE test (pk int primary key)",
"INSERT INTO test VALUES (1),(2),(3)",
"CALL dolt_commit('-Am', 'new table')",
"INSERT INTO test VALUES (4)",
"CALL dolt_commit('-am', 'added row 4')",
"INSERT INTO test VALUES (5)",
"CALL dolt_commit('-am', 'added row 5')",
},
asserts: []assertion{
{
query: "SELECT count(*) FROM dolt_log",
expected: []sql.Row{{int64(4)}},
},
{
query: "SELECT count(*) FROM `dolt/dolt_migrated_commits`.dolt_commit_mapping",
expected: []sql.Row{{int64(4)}},
},
{
query: "SELECT count(*) FROM `dolt/dolt_migrated_commits`.dolt_commit_mapping WHERE new_commit_hash IN (SELECT commit_hash FROM dolt_log)",
expected: []sql.Row{{int64(4)}},
},
{
query: "SELECT count(*) FROM `dolt/dolt_migrated_commits`.dolt_commit_mapping WHERE new_commit_hash NOT IN (SELECT commit_hash FROM dolt_log)",
expected: []sql.Row{{int64(0)}},
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := context.Background()
preEnv := setupMigrationTest(t, ctx, test)
postEnv := runMigration(t, ctx, preEnv)
root, err := postEnv.WorkingRoot(ctx)
require.NoError(t, err)
for _, a := range test.asserts {
actual, err := sqle.ExecuteSelect(ctx, postEnv, root, a.query)
assert.NoError(t, err)
assert.Equal(t, a.expected, actual)
}
})
}
}
func setupMigrationTest(t *testing.T, ctx context.Context, test migrationTest) *env.DoltEnv {
dEnv := dtestutils.CreateTestEnv()
// run setup hook before other setup queries
if test.hook != nil {
var err error
dEnv, err = test.hook(ctx, dEnv)
require.NoError(t, err)
}
cliCtx, err := commands.NewArgFreeCliContext(ctx, dEnv, dEnv.FS)
require.NoError(t, err)
defer cliCtx.Close()
cmd := commands.SqlCmd{}
for _, query := range test.setup {
code := cmd.Exec(ctx, cmd.Name(), []string{"-q", query}, dEnv, cliCtx)
require.Equal(t, 0, code)
}
return dEnv
}
func SetupHookRefKeys(ctx context.Context, dEnv *env.DoltEnv) (*env.DoltEnv, error) {
pk, _ := schema.NewColumnWithTypeInfo("pk", 1, typeinfo.TextType, true, "", false, "", schema.NotNullConstraint{})
c0, _ := schema.NewColumnWithTypeInfo("c0", 2, typeinfo.Int32Type, false, "", false, "")
c1, _ := schema.NewColumnWithTypeInfo("c1", 3, typeinfo.BlobType, false, "", false, "")
sch, err := schema.SchemaFromCols(schema.NewColCollection(pk, c0, c1))
if err != nil {
return nil, err
}
_, err = sch.Indexes().AddIndexByColNames("blob_idx", []string{"c1"}, nil, schema.IndexProperties{IsUserDefined: true})
if err != nil {
return nil, err
}
ws, err := dEnv.WorkingSet(ctx)
if err != nil {
return nil, err
}
root, err := doltdb.CreateEmptyTable(ctx, ws.WorkingRoot(), doltdb.TableName{Name: "test"}, sch)
if err != nil {
return nil, err
}
if err = dEnv.UpdateWorkingSet(ctx, ws.WithWorkingRoot(root)); err != nil {
return nil, err
}
return dEnv, nil
}
func runMigration(t *testing.T, ctx context.Context, preEnv *env.DoltEnv) (postEnv *env.DoltEnv) {
ddb, err := initTestMigrationDB(ctx)
require.NoError(t, err)
postEnv = env.NewDoltEnv(
preEnv.Version,
preEnv.Config,
preEnv.RepoState,
ddb,
preEnv.FS,
)
err = migrate.TraverseDAG(ctx, migrate.Environment{}, preEnv.DoltDB(ctx), postEnv.DoltDB(ctx))
assert.NoError(t, err)
return
}
func initTestMigrationDB(ctx context.Context) (*doltdb.DoltDB, error) {
var db datas.Database
storage := &chunks.MemoryStorage{}
cs := storage.NewViewWithFormat("__DOLT__")
vrw := types.NewValueStore(cs)
ns := tree.NewNodeStore(cs)
db = datas.NewTypesDatabase(vrw, ns)
name, email := "user", "user@fake.horse"
meta, err := datas.NewCommitMeta(name, email, "test migration")
if err != nil {
return nil, err
}
rv, err := doltdb.EmptyRootValue(ctx, vrw, ns)
if err != nil {
return nil, err
}
ds, err := db.GetDataset(ctx, ref.NewInternalRef("migration").String())
if err != nil {
return nil, err
}
_, err = db.Commit(ctx, ds, rv.NomsValue(), datas.CommitOptions{Meta: meta})
if err != nil {
return nil, err
}
ddb, err := doltdb.DoltDBFromCS(cs, "")
if err != nil {
return nil, err
}
return ddb, nil
}
-321
View File
@@ -1,321 +0,0 @@
// Copyright 2022 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 migrate
import (
"context"
"fmt"
"io"
"time"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/store/datas"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/store/chunks"
"github.com/dolthub/dolt/go/store/hash"
"github.com/dolthub/dolt/go/store/pool"
"github.com/dolthub/dolt/go/store/prolly"
"github.com/dolthub/dolt/go/store/prolly/shim"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/types"
"github.com/dolthub/dolt/go/store/val"
)
const (
MigratedCommitsBranch = "dolt_migrated_commits"
MigratedCommitsTable = "dolt_commit_mapping"
)
var (
mappingSchema, _ = schema.SchemaFromCols(schema.NewColCollection(
schema.NewColumn("old_commit_hash", 0, types.StringKind, true),
schema.NewColumn("new_commit_hash", 1, types.StringKind, false),
))
desc = val.NewTupleDescriptor(val.Type{Enc: val.StringEnc, Nullable: false})
)
// progress maintains the state of migration.
type progress struct {
stack []*doltdb.Commit
// mapping tracks migrated commits
// it maps old commit hash to new hash
mapping *prolly.MutableMap
kb, vb *val.TupleBuilder
buffPool pool.BuffPool
vs *types.ValueStore
cs chunks.ChunkStore
}
func newProgress(ctx context.Context, cs chunks.ChunkStore) (*progress, error) {
kd := val.NewTupleDescriptor(val.Type{
Enc: val.ByteStringEnc,
Nullable: false,
})
vd := val.NewTupleDescriptor(val.Type{
Enc: val.ByteStringEnc,
Nullable: false,
})
ns := tree.NewNodeStore(cs)
vs := types.NewValueStore(cs)
mapping, err := prolly.NewMapFromTuples(ctx, ns, kd, vd)
if err != nil {
return nil, err
}
mut := mapping.Mutate()
kb := val.NewTupleBuilder(kd, ns)
vb := val.NewTupleBuilder(vd, ns)
return &progress{
stack: make([]*doltdb.Commit, 0, 128),
mapping: mut,
kb: kb,
vb: vb,
buffPool: ns.Pool(),
vs: vs,
cs: cs,
}, nil
}
func (p *progress) Has(ctx context.Context, addr hash.Hash) (ok bool, err error) {
p.kb.PutByteString(0, addr[:])
k, err := p.kb.Build(p.buffPool)
if err != nil {
return false, err
}
return p.mapping.Has(ctx, k)
}
func (p *progress) Get(ctx context.Context, old hash.Hash) (new hash.Hash, err error) {
p.kb.PutByteString(0, old[:])
k, err := p.kb.Build(p.buffPool)
if err != nil {
return new, err
}
err = p.mapping.Get(ctx, k, func(_, v val.Tuple) error {
if len(v) > 0 {
n, ok := p.vb.Desc.GetBytes(0, v)
if !ok {
return fmt.Errorf("failed to get string address from commit mapping value")
}
new = hash.New(n)
}
return nil
})
return new, err
}
func (p *progress) Put(ctx context.Context, old, new hash.Hash) (err error) {
p.kb.PutByteString(0, old[:])
k, err := p.kb.Build(p.buffPool)
if err != nil {
return err
}
p.vb.PutByteString(0, new[:])
v, err := p.vb.Build(p.buffPool)
if err != nil {
return err
}
err = p.mapping.Put(ctx, k, v)
return
}
func (p *progress) Push(ctx context.Context, cm *doltdb.Commit) (err error) {
p.stack = append(p.stack, cm)
return
}
func (p *progress) Pop(ctx context.Context) (cm *doltdb.Commit, err error) {
if len(p.stack) == 0 {
return nil, nil
}
top := len(p.stack) - 1
cm = p.stack[top]
p.stack = p.stack[:top]
return
}
func (p *progress) Log(ctx context.Context, format string, args ...any) {
cli.Println(time.Now().UTC().String() + " " + fmt.Sprintf(format, args...))
}
func (p *progress) Finalize(ctx context.Context) (prolly.Map, error) {
m, err := p.mapping.Map(ctx)
if err != nil {
return prolly.Map{}, err
}
v := shim.ValueFromMap(m)
ref, err := p.vs.WriteValue(ctx, v)
if err != nil {
return prolly.Map{}, err
}
last, err := p.vs.Root(ctx)
if err != nil {
return prolly.Map{}, err
}
ok, err := p.vs.Commit(ctx, last, last)
if err != nil {
return prolly.Map{}, err
} else if !ok {
return prolly.Map{}, fmt.Errorf("failed to commit, manifest swapped out beneath us")
}
p.Log(ctx, "Wrote commit mapping!! [commit_mapping_ref: %s]", ref.TargetHash().String())
p.Log(ctx, "Commit mapping allow mapping pre-migration commit hashes to post-migration commit hashes, "+
"it is available on branch '%s' in table '%s'", MigratedCommitsBranch, MigratedCommitsTable)
return m, nil
}
func persistMigratedCommitMapping(ctx context.Context, ddb *doltdb.DoltDB, mapping prolly.Map) error {
// create a new branch to persist the migrated commit mapping
init, err := ddb.ResolveCommitRef(ctx, ref.NewInternalRef(doltdb.CreationBranch))
if err != nil {
return err
}
br := ref.NewBranchRef(MigratedCommitsBranch)
err = ddb.NewBranchAtCommit(ctx, br, init, nil)
if err != nil {
return err
}
ns, vrw := ddb.NodeStore(), ddb.ValueReadWriter()
m, err := prolly.NewMapFromTuples(ctx, ns, desc, desc)
if err != nil {
return err
}
rows := m.Mutate()
bld := val.NewTupleBuilder(desc, ns)
// convert |mapping| values from hash.Hash to string
iter, err := mapping.IterAll(ctx)
if err != nil {
return err
}
var k, v val.Tuple
kd, vd := mapping.Descriptors()
for {
k, v, err = iter.Next(ctx)
if err == io.EOF {
break
} else if err != nil {
return err
}
o, _ := kd.GetBytes(0, k)
bld.PutString(0, hash.New(o).String())
key, err := bld.Build(ddb.NodeStore().Pool())
if err != nil {
return err
}
n, _ := vd.GetBytes(0, v)
bld.PutString(0, hash.New(n).String())
value, err := bld.Build(ddb.NodeStore().Pool())
if err != nil {
return err
}
if err = rows.Put(ctx, key, value); err != nil {
return err
}
}
m, err = rows.Map(ctx)
if err != nil {
return err
}
idx := durable.IndexFromProllyMap(m)
tbl, err := doltdb.NewTable(ctx, vrw, ns, mappingSchema, idx, nil, nil)
if err != nil {
return err
}
root, err := init.GetRootValue(ctx)
if err != nil {
return err
}
root, err = root.PutTable(ctx, doltdb.TableName{Name: MigratedCommitsTable}, tbl)
if err != nil {
return err
}
return commitRoot(ctx, ddb, br, root, init)
}
func commitRoot(
ctx context.Context,
ddb *doltdb.DoltDB,
br ref.BranchRef,
root doltdb.RootValue,
parent *doltdb.Commit,
) error {
roots := doltdb.Roots{
Head: root,
Working: root,
Staged: root,
}
parents := []*doltdb.Commit{parent}
meta, err := parent.GetCommitMeta(ctx)
if err != nil {
return err
}
meta, err = datas.NewCommitMeta(meta.Name, meta.Email, meta.Description)
if err != nil {
return err
}
pcm, err := ddb.NewPendingCommit(ctx, roots, parents, false, meta)
if err != nil {
return err
}
wsr, err := ref.WorkingSetRefForHead(br)
if err != nil {
return err
}
ws, err := ddb.ResolveWorkingSet(ctx, wsr)
if err != nil {
return err
}
prev, err := ws.HashOf()
if err != nil {
return err
}
ws = ws.WithWorkingRoot(root).WithStagedRoot(root)
_, err = ddb.CommitWithWorkingSet(ctx, br, wsr, pcm, ws, prev, &datas.WorkingSetMeta{
Name: meta.Name,
Email: meta.Email,
Timestamp: uint64(time.Now().Unix()),
}, nil)
return err
}
-725
View File
@@ -1,725 +0,0 @@
// Copyright 2022 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 migrate
import (
"context"
"fmt"
"github.com/dolthub/go-mysql-server/sql"
gmstypes "github.com/dolthub/go-mysql-server/sql/types"
"github.com/dolthub/vitess/go/vt/proto/query"
"golang.org/x/sync/errgroup"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
"github.com/dolthub/dolt/go/libraries/utils/set"
"github.com/dolthub/dolt/go/store/chunks"
"github.com/dolthub/dolt/go/store/datas"
"github.com/dolthub/dolt/go/store/hash"
"github.com/dolthub/dolt/go/store/prolly"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/types"
"github.com/dolthub/dolt/go/store/val"
)
var (
flushRef = ref.NewInternalRef("migration-flush")
)
func migrateWorkingSet(ctx context.Context, menv Environment, brRef ref.BranchRef, wsRef ref.WorkingSetRef, old, new *doltdb.DoltDB) error {
oldHead, err := old.ResolveCommitRef(ctx, brRef)
if err != nil {
return err
}
oldHeadRoot, err := oldHead.GetRootValue(ctx)
if err != nil {
return err
}
oldWs, err := old.ResolveWorkingSet(ctx, wsRef)
if err == doltdb.ErrWorkingSetNotFound {
// If a branch was created prior to dolt version 0.26.10, no working set will exist for it.
// In this case, we will pretend it exists with the same root as the head commit.
oldWs = doltdb.EmptyWorkingSet(wsRef)
oldWs = oldWs.WithWorkingRoot(oldHeadRoot).WithStagedRoot(oldHeadRoot)
} else if err != nil {
return err
}
newHead, err := new.ResolveCommitRef(ctx, brRef)
if err != nil {
return err
}
newHeadRoot, err := newHead.GetRootValue(ctx)
if err != nil {
return err
}
wr, err := migrateRoot(ctx, menv, oldHeadRoot, oldWs.WorkingRoot(), newHeadRoot)
if err != nil {
return err
}
sr, err := migrateRoot(ctx, menv, oldHeadRoot, oldWs.StagedRoot(), newHeadRoot)
if err != nil {
return err
}
err = validateRootValue(ctx, oldHeadRoot, oldWs.WorkingRoot(), wr)
if err != nil {
return err
}
err = validateRootValue(ctx, oldHeadRoot, oldWs.StagedRoot(), sr)
if err != nil {
return err
}
newWs := doltdb.EmptyWorkingSet(wsRef).WithWorkingRoot(wr).WithStagedRoot(sr)
return new.UpdateWorkingSet(ctx, wsRef, newWs, hash.Hash{}, oldWs.Meta(), nil)
}
func migrateCommit(ctx context.Context, menv Environment, oldCm *doltdb.Commit, new *doltdb.DoltDB, prog *progress) error {
oldHash, err := oldCm.HashOf()
if err != nil {
return err
}
ok, err := prog.Has(ctx, oldHash)
if err != nil {
return err
} else if ok {
return nil
}
if oldCm.NumParents() == 0 {
return migrateInitCommit(ctx, oldCm, new, prog)
}
hs := oldHash.String()
prog.Log(ctx, "migrating commit %s", hs)
oldRoot, err := oldCm.GetRootValue(ctx)
if err != nil {
return err
}
optCmt, err := oldCm.GetParent(ctx, 0)
if err != nil {
return err
}
oldParentCm, ok := optCmt.ToCommit()
if !ok {
return doltdb.ErrGhostCommitEncountered
}
oldParentRoot, err := oldParentCm.GetRootValue(ctx)
if err != nil {
return err
}
oph, err := oldParentCm.HashOf()
if err != nil {
return err
}
ok, err = prog.Has(ctx, oph)
if err != nil {
return err
} else if !ok {
return fmt.Errorf("cannot find commit mapping for Commit (%s)", oph.String())
}
newParentAddr, err := prog.Get(ctx, oph)
if err != nil {
return err
}
optCmt, err = new.ReadCommit(ctx, newParentAddr)
if err != nil {
return err
}
newParentCm, ok := optCmt.ToCommit()
if !ok {
return doltdb.ErrGhostCommitEncountered
}
newParentRoot, err := newParentCm.GetRootValue(ctx)
if err != nil {
return err
}
mRoot, err := migrateRoot(ctx, menv, oldParentRoot, oldRoot, newParentRoot)
if err != nil {
return err
}
_, addr, err := new.WriteRootValue(ctx, mRoot)
if err != nil {
return err
}
value, err := new.ValueReadWriter().ReadValue(ctx, addr)
if err != nil {
return err
}
opts, err := migrateCommitOptions(ctx, oldCm, prog)
if err != nil {
return err
}
migratedCm, err := new.CommitDangling(ctx, value, opts)
if err != nil {
return err
}
// update progress
newHash, err := migratedCm.HashOf()
if err != nil {
return err
}
if err = prog.Put(ctx, oldHash, newHash); err != nil {
return err
}
// flush ChunkStore
if err = new.SetHead(ctx, flushRef, newHash); err != nil {
return err
}
err = new.ShallowGC(ctx)
if err != nil && err != chunks.ErrUnsupportedOperation {
return err
}
// validate root after we flush the ChunkStore to facilitate
// investigating failed migrations
if err = validateRootValue(ctx, oldParentRoot, oldRoot, mRoot); err != nil {
return err
}
return nil
}
func migrateInitCommit(ctx context.Context, cm *doltdb.Commit, new *doltdb.DoltDB, prog *progress) error {
oldHash, err := cm.HashOf()
if err != nil {
return err
}
rv, err := doltdb.EmptyRootValue(ctx, new.ValueReadWriter(), new.NodeStore())
if err != nil {
return err
}
meta, err := cm.GetCommitMeta(ctx)
if err != nil {
return err
}
datasDB := doltdb.HackDatasDatabaseFromDoltDB(new)
creation := ref.NewInternalRef(doltdb.CreationBranch)
ds, err := datasDB.GetDataset(ctx, creation.String())
if err != nil {
return err
}
ds, err = datasDB.Commit(ctx, ds, rv.NomsValue(), datas.CommitOptions{Meta: meta})
if err != nil {
return err
}
newCm, err := new.ResolveCommitRef(ctx, creation)
if err != nil {
return err
}
newHash, err := newCm.HashOf()
if err != nil {
return err
}
return prog.Put(ctx, oldHash, newHash)
}
func migrateCommitOptions(ctx context.Context, oldCm *doltdb.Commit, prog *progress) (datas.CommitOptions, error) {
parents, err := oldCm.ParentHashes(ctx)
if err != nil {
return datas.CommitOptions{}, err
}
if len(parents) == 0 {
panic("expected non-zero parents list")
}
for i := range parents {
migrated, err := prog.Get(ctx, parents[i])
if err != nil {
return datas.CommitOptions{}, err
}
parents[i] = migrated
}
meta, err := oldCm.GetCommitMeta(ctx)
if err != nil {
return datas.CommitOptions{}, err
}
return datas.CommitOptions{
Parents: parents,
Meta: meta,
}, nil
}
func migrateRoot(ctx context.Context, menv Environment, oldParent, oldRoot, newParent doltdb.RootValue) (doltdb.RootValue, error) {
migrated := newParent
fkc, err := oldRoot.GetForeignKeyCollection(ctx)
if err != nil {
return nil, err
}
migrated, err = migrated.PutForeignKeyCollection(ctx, fkc)
if err != nil {
return nil, err
}
removedTables, err := getRemovedTableNames(ctx, oldParent, oldRoot)
if err != nil {
return nil, err
}
migrated, err = migrated.RemoveTables(ctx, true, false, doltdb.ToTableNames(removedTables, doltdb.DefaultSchemaName)...)
if err != nil {
return nil, err
}
err = oldRoot.IterTables(ctx, func(name doltdb.TableName, oldTbl *doltdb.Table, sch schema.Schema) (bool, error) {
ok, err := oldTbl.HasConflicts(ctx)
if err != nil {
return true, err
} else if ok && !menv.DropConflicts {
return true, fmt.Errorf("cannot migrate table with conflicts (%s)", name)
}
// TODO: schema names
newSch, err := migrateSchema(ctx, name.Name, sch)
if err != nil {
return true, err
}
if err = validateSchema(newSch); err != nil {
return true, err
}
// if there was a schema change in this commit,
// diff against an empty table and rewrite everything
var parentSch schema.Schema
oldParentTbl, ok, err := oldParent.GetTable(ctx, name)
if err != nil {
return true, err
}
if ok {
parentSch, err = oldParentTbl.GetSchema(ctx)
if err != nil {
return true, err
}
}
if !ok || !schema.SchemasAreEqual(sch, parentSch) {
// provide empty table to diff against
oldParentTbl, err = doltdb.NewEmptyTable(ctx, oldParent.VRW(), oldParent.NodeStore(), sch)
if err != nil {
return true, err
}
}
newParentTbl, ok, err := newParent.GetTable(ctx, name)
if err != nil {
return true, err
}
if !ok || !schema.SchemasAreEqual(sch, parentSch) {
// provide empty table to diff against
newParentTbl, err = doltdb.NewEmptyTable(ctx, newParent.VRW(), newParent.NodeStore(), newSch)
if err != nil {
return true, err
}
}
mtbl, err := migrateTable(ctx, newSch, oldParentTbl, oldTbl, newParentTbl)
if err != nil {
return true, err
}
migrated, err = migrated.PutTable(ctx, name, mtbl)
if err != nil {
return true, err
}
return false, nil
})
if err != nil {
return nil, err
}
return migrated, nil
}
// renames also get returned here
func getRemovedTableNames(ctx context.Context, prev, curr doltdb.RootValue) ([]string, error) {
prevNames, err := prev.GetTableNames(ctx, doltdb.DefaultSchemaName, true)
if err != nil {
return nil, err
}
tblNameSet := set.NewStrSet(prevNames)
currNames, err := curr.GetTableNames(ctx, doltdb.DefaultSchemaName, true)
if err != nil {
return nil, err
}
tblNameSet.Remove(currNames...)
return tblNameSet.AsSlice(), nil
}
func migrateTable(ctx context.Context, newSch schema.Schema, oldParentTbl, oldTbl, newParentTbl *doltdb.Table) (*doltdb.Table, error) {
idx, err := oldParentTbl.GetRowData(ctx)
if err != nil {
return nil, err
}
oldParentRows := durable.NomsMapFromIndex(idx)
idx, err = oldTbl.GetRowData(ctx)
if err != nil {
return nil, err
}
oldRows := durable.NomsMapFromIndex(idx)
idx, err = newParentTbl.GetRowData(ctx)
if err != nil {
return nil, err
}
newParentRows, err := durable.ProllyMapFromIndex(idx)
if err != nil {
return nil, err
}
oldParentSet, err := oldParentTbl.GetIndexSet(ctx)
if err != nil {
return nil, err
}
oldSet, err := oldTbl.GetIndexSet(ctx)
if err != nil {
return nil, err
}
newParentSet, err := newParentTbl.GetIndexSet(ctx)
if err != nil {
return nil, err
}
var newRows durable.Index
var newSet durable.IndexSet
originalCtx := ctx
eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error {
var merr error
newRows, merr = migrateIndex(ctx, newSch, oldParentRows, oldRows, newParentRows, newParentTbl.NodeStore())
return merr
})
vrw, ns := newParentTbl.ValueReadWriter(), newParentTbl.NodeStore()
eg.Go(func() error {
var merr error
newSet, merr = migrateIndexSet(ctx, newSch, oldParentSet, oldSet, newParentSet, vrw, ns)
return merr
})
if err = eg.Wait(); err != nil {
return nil, err
}
ai, err := oldTbl.GetAutoIncrementValue(originalCtx)
if err != nil {
return nil, err
}
autoInc := types.Uint(ai)
return doltdb.NewTable(originalCtx, vrw, ns, newSch, newRows, newSet, autoInc)
}
func migrateSchema(ctx context.Context, tableName string, existing schema.Schema) (schema.Schema, error) {
// dolt_schemas and dolt_docs previously included columns with
// SQL type TEXT, but NomsKind of StringKind
if doltdb.HasDoltPrefix(tableName) {
var patched bool
cols := existing.GetAllCols().GetColumns()
for i, c := range cols {
qt := c.TypeInfo.ToSqlType().Type()
if qt == query.Type_TEXT && c.Kind == types.StringKind {
// NewColumn picks SQL type from NomsKind, converting this TEXT column to VARCHAR
cols[i] = schema.NewColumn(c.Name, c.Tag, c.Kind, c.IsPartOfPK, c.Constraints...)
patched = true
}
}
if patched {
allCols := schema.NewColCollection(cols...)
schema.NewIndexCollection(allCols, existing.GetPKCols())
return schema.NewSchema(
allCols,
existing.GetPkOrdinals(),
existing.GetCollation(),
existing.Indexes(),
existing.Checks(),
)
}
return existing, nil
}
// Blob types cannot be index keys in the new format:
// substitute VARCHAR(max) for TEXT, VARBINARY(max) for BLOB
// TODO: print warning to users
var patched bool
tags := schema.GetKeyColumnTags(existing)
cols := existing.GetAllCols().GetColumns()
for i, c := range cols {
if tags.Contains(c.Tag) {
var err error
switch c.TypeInfo.ToSqlType().Type() {
case query.Type_TEXT:
patched = true
info := typeinfo.StringDefaultType
cols[i], err = schema.NewColumnWithTypeInfo(c.Name, c.Tag, info, c.IsPartOfPK, c.Default, c.AutoIncrement, c.Comment, c.Constraints...)
case query.Type_BLOB:
patched = true
info := typeinfo.VarbinaryDefaultType
cols[i], err = schema.NewColumnWithTypeInfo(c.Name, c.Tag, info, c.IsPartOfPK, c.Default, c.AutoIncrement, c.Comment, c.Constraints...)
}
if err != nil {
return nil, err
}
}
}
// String types are sorted using a binary collation in __LD_1__
// force-set collation to utf8mb4_0900_bin to match the order
for i, c := range cols {
st, ok := c.TypeInfo.ToSqlType().(sql.StringType)
if !ok {
continue
}
patched = true
var err error
switch st.Type() {
case query.Type_CHAR, query.Type_VARCHAR, query.Type_TEXT:
st, err = gmstypes.CreateString(st.Type(), st.Length(), sql.Collation_utf8mb4_0900_bin)
case query.Type_BINARY, query.Type_VARBINARY, query.Type_BLOB:
st, err = gmstypes.CreateString(st.Type(), st.Length(), sql.Collation_binary)
}
if err != nil {
return nil, err
}
info, err := typeinfo.FromSqlType(st)
if err != nil {
return nil, err
}
cols[i], err = schema.NewColumnWithTypeInfo(c.Name, c.Tag, info, c.IsPartOfPK, c.Default, c.AutoIncrement, c.Comment, c.Constraints...)
if err != nil {
return nil, err
}
}
if !patched {
return existing, nil
}
sch, err := schema.NewSchema(
schema.NewColCollection(cols...),
existing.GetPkOrdinals(),
existing.GetCollation(),
existing.Indexes(),
existing.Checks(),
)
if err != nil {
return nil, err
}
return sch, nil
}
func migrateIndexSet(
ctx context.Context,
sch schema.Schema,
oldParentSet, oldSet, newParentSet durable.IndexSet,
vrw types.ValueReadWriter, ns tree.NodeStore,
) (durable.IndexSet, error) {
newSet, err := durable.NewIndexSet(ctx, vrw, ns)
if err != nil {
return nil, err
}
for _, def := range sch.Indexes().AllIndexes() {
idx, err := oldParentSet.GetIndex(ctx, sch, nil, def.Name())
if err != nil {
return nil, err
}
oldParent := durable.NomsMapFromIndex(idx)
idx, err = oldSet.GetIndex(ctx, sch, nil, def.Name())
if err != nil {
return nil, err
}
old := durable.NomsMapFromIndex(idx)
idx, err = newParentSet.GetIndex(ctx, sch, nil, def.Name())
if err != nil {
return nil, err
}
newParent, err := durable.ProllyMapFromIndex(idx)
if err != nil {
return nil, err
}
newIdx, err := migrateIndex(ctx, def.Schema(), oldParent, old, newParent, ns)
if err != nil {
return nil, err
}
newSet, err = newSet.PutIndex(ctx, def.Name(), newIdx)
if err != nil {
return nil, err
}
}
return newSet, nil
}
func migrateIndex(
ctx context.Context,
sch schema.Schema,
oldParent, oldMap types.Map,
newParent prolly.Map,
ns tree.NodeStore,
) (durable.Index, error) {
eg, ctx := errgroup.WithContext(ctx)
differ := make(chan types.ValueChanged, 256)
writer := make(chan val.Tuple, 256)
kt, vt := tupleTranslatorsFromSchema(sch, ns)
// read old noms map
eg.Go(func() error {
defer close(differ)
return oldMap.Diff(ctx, oldParent, differ)
})
// translate noms tuples to prolly tuples
eg.Go(func() error {
defer close(writer)
return translateTuples(ctx, kt, vt, differ, writer)
})
var newMap prolly.Map
// write tuples in new prolly map
eg.Go(func() (err error) {
newMap, err = writeProllyMap(ctx, newParent, writer)
return
})
if err := eg.Wait(); err != nil {
return nil, err
}
return durable.IndexFromProllyMap(newMap), nil
}
func translateTuples(ctx context.Context, kt, vt translator, differ <-chan types.ValueChanged, writer chan<- val.Tuple) error {
for {
var (
diff types.ValueChanged
newKey val.Tuple
newVal val.Tuple
ok bool
err error
)
select {
case diff, ok = <-differ:
if !ok {
return nil // done
}
case _ = <-ctx.Done():
return ctx.Err()
}
switch diff.ChangeType {
case types.DiffChangeAdded:
fallthrough
case types.DiffChangeModified:
newVal, err = vt.TranslateTuple(ctx, diff.NewValue.(types.Tuple))
if err != nil {
return err
}
fallthrough
case types.DiffChangeRemoved:
newKey, err = kt.TranslateTuple(ctx, diff.Key.(types.Tuple))
if err != nil {
return err
}
}
select {
case writer <- newKey:
case _ = <-ctx.Done():
return ctx.Err()
}
select {
case writer <- newVal:
case _ = <-ctx.Done():
return ctx.Err()
}
}
}
func writeProllyMap(ctx context.Context, prev prolly.Map, writer <-chan val.Tuple) (m prolly.Map, err error) {
var (
k, v val.Tuple
ok bool
)
mut := prev.Mutate()
for {
select {
case k, ok = <-writer:
if !ok {
m, err = mut.Map(ctx)
return // done
}
case <-ctx.Done():
return
}
select {
case v, ok = <-writer:
assertTrue(ok)
case <-ctx.Done():
return
}
if err = mut.Put(ctx, k, v); err != nil {
return
}
}
}
-213
View File
@@ -1,213 +0,0 @@
// Copyright 2022 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 migrate
import (
"context"
"fmt"
"github.com/dolthub/dolt/go/store/datas"
"github.com/dolthub/dolt/go/store/hash"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
)
// TraverseDAG traverses |old|, migrating values to |new|.
func TraverseDAG(ctx context.Context, menv Environment, old, new *doltdb.DoltDB) (err error) {
var heads []ref.DoltRef
var prog *progress
heads, err = old.GetHeadRefs(ctx)
if err != nil {
return err
}
datasdb := doltdb.HackDatasDatabaseFromDoltDB(new)
cs := datas.ChunkStoreFromDatabase(datasdb)
prog, err = newProgress(ctx, cs)
if err != nil {
return err
}
for i := range heads {
if err = traverseRefHistory(ctx, menv, heads[i], old, new, prog); err != nil {
return err
}
}
if err = validateBranchMapping(ctx, old, new); err != nil {
return err
}
// write the migrated commit mapping to a special branch
m, err := prog.Finalize(ctx)
if err != nil {
return err
}
if err = persistMigratedCommitMapping(ctx, new, m); err != nil {
return err
}
if err = old.Close(); err != nil {
return err
}
if err = new.Close(); err != nil {
return err
}
return
}
func traverseRefHistory(ctx context.Context, menv Environment, r ref.DoltRef, old, new *doltdb.DoltDB, prog *progress) error {
switch r.GetType() {
case ref.BranchRefType:
if err := traverseBranchHistory(ctx, menv, r, old, new, prog); err != nil {
return err
}
wsRef, err := ref.WorkingSetRefForHead(r)
if err != nil {
return err
}
return migrateWorkingSet(ctx, menv, r.(ref.BranchRef), wsRef, old, new)
case ref.TagRefType:
return traverseTagHistory(ctx, menv, r.(ref.TagRef), old, new, prog)
case ref.RemoteRefType:
return traverseBranchHistory(ctx, menv, r, old, new, prog)
case ref.WorkspaceRefType, ref.InternalRefType:
return nil
default:
panic(fmt.Sprintf("unknown ref type %s", r.String()))
}
}
func traverseBranchHistory(ctx context.Context, menv Environment, r ref.DoltRef, old, new *doltdb.DoltDB, prog *progress) error {
cm, err := old.ResolveCommitRef(ctx, r)
if err != nil {
return err
}
if err = traverseCommitHistory(ctx, menv, cm, new, prog); err != nil {
return err
}
oldHash, err := cm.HashOf()
if err != nil {
return err
}
newHash, err := prog.Get(ctx, oldHash)
if err != nil {
return err
}
return new.SetHead(ctx, r, newHash)
}
func traverseTagHistory(ctx context.Context, menv Environment, r ref.TagRef, old, new *doltdb.DoltDB, prog *progress) error {
t, err := old.ResolveTag(ctx, r)
if err != nil {
return err
}
if err = traverseCommitHistory(ctx, menv, t.Commit, new, prog); err != nil {
return err
}
oldHash, err := t.Commit.HashOf()
if err != nil {
return err
}
newHash, err := prog.Get(ctx, oldHash)
if err != nil {
return err
}
optCmt, err := new.ReadCommit(ctx, newHash)
if err != nil {
return err
}
cm, ok := optCmt.ToCommit()
if !ok {
return doltdb.ErrGhostCommitEncountered
}
return new.NewTagAtCommit(ctx, r, cm, t.Meta)
}
func traverseCommitHistory(ctx context.Context, menv Environment, cm *doltdb.Commit, new *doltdb.DoltDB, prog *progress) error {
ch, err := cm.HashOf()
if err != nil {
return err
}
ok, err := prog.Has(ctx, ch)
if err != nil || ok {
return err
}
for {
ph, err := cm.ParentHashes(ctx)
if err != nil {
return err
}
idx, err := firstAbsent(ctx, prog, ph)
if err != nil {
return err
}
if idx < 0 {
// parents for |cm| are done, migrate |cm|
if err = migrateCommit(ctx, menv, cm, new, prog); err != nil {
return err
}
// pop the stack, traverse upwards
cm, err = prog.Pop(ctx)
if err != nil {
return err
}
if cm == nil {
return nil // done
}
continue
}
// push the stack, traverse downwards
if err = prog.Push(ctx, cm); err != nil {
return err
}
optCmt, err := cm.GetParent(ctx, idx)
if err != nil {
return err
}
cm, ok = optCmt.ToCommit()
if !ok {
return doltdb.ErrGhostCommitEncountered
}
}
}
func firstAbsent(ctx context.Context, p *progress, addrs []hash.Hash) (int, error) {
for i := range addrs {
ok, err := p.Has(ctx, addrs[i])
if err != nil {
return -1, err
}
if !ok {
return i, nil
}
}
return -1, nil
}
-402
View File
@@ -1,402 +0,0 @@
// Copyright 2022 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 migrate
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"strings"
"time"
"github.com/shopspring/decimal"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/json"
"github.com/dolthub/dolt/go/store/pool"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/types"
"github.com/dolthub/dolt/go/store/val"
)
const (
maxInlineValue = 16383
)
var ErrCannotMigrateText = errors.New("could not migrate TEXT value to VARCHAR, TEXT value exceeds 16383 size limit")
var ErrCannotMigrateBlob = errors.New("could not migrate BLOB value to VARBINARY, BLOB value exceeds 16383 size limit")
type translator struct {
builder *val.TupleBuilder
// maps columns tags to ordinal position
mapping map[uint64]int
ns tree.NodeStore
pool pool.BuffPool
}
func tupleTranslatorsFromSchema(sch schema.Schema, ns tree.NodeStore) (kt, vt translator) {
kd := sch.GetKeyDescriptor(ns)
vd := sch.GetValueDescriptor(ns)
keyMap := sch.GetPKCols().TagToIdx
valMap := sch.GetNonPKCols().TagToIdx
if !schema.IsKeyless(sch) {
kt = newTupleTranslator(ns, keyMap, kd)
vt = newTupleTranslator(ns, valMap, vd)
return
}
// for keyless tables, we must account for the id and cardinality columns
keyMap2 := map[uint64]int{schema.KeylessRowIdTag: 0}
valMap2 := map[uint64]int{schema.KeylessRowCardinalityTag: 0}
// shift positions for other columns
for tag, pos := range valMap {
valMap2[tag] = pos + 1
}
// assert previous keyMap was empty
assertTrue(len(keyMap) == 0)
kt = newTupleTranslator(ns, keyMap2, kd)
vt = newTupleTranslator(ns, valMap2, vd)
return
}
func newTupleTranslator(ns tree.NodeStore, mapping map[uint64]int, desc *val.TupleDesc) translator {
return translator{
builder: val.NewTupleBuilder(desc, ns),
mapping: mapping,
ns: ns,
pool: pool.NewBuffPool(),
}
}
// TranslateTuple translates a types.Tuple into a val.Tuple.
func (t translator) TranslateTuple(ctx context.Context, tup types.Tuple) (val.Tuple, error) {
if !isEven(tup.Len()) {
return nil, fmt.Errorf("expected even-legnth tuple (len %d)", tup.Len())
}
var tag uint64
err := tup.IterFields(func(i uint64, value types.Value) (stop bool, err error) {
// even fields are column tags, odd fields are column values
if isEven(i) {
tag = uint64(value.(types.Uint))
} else {
// |tag| set in previous iteration
pos, ok := t.mapping[tag]
if ok {
err = translateNomsField(ctx, t.ns, value, pos, t.builder)
stop = err != nil
} // else tombstone column
}
return
})
if err != nil {
return nil, err
}
defer func() {
if r := recover(); r != nil {
panic(tup.String())
}
}()
return t.builder.Build(t.pool)
}
func translateNomsField(ctx context.Context, ns tree.NodeStore, value types.Value, idx int, b *val.TupleBuilder) error {
nk := value.Kind()
switch nk {
case types.NullKind:
return nil // todo(andy): log warning?
case types.UintKind:
translateUintField(value.(types.Uint), idx, b)
case types.IntKind:
translateIntField(value.(types.Int), idx, b)
case types.FloatKind:
translateFloatField(value.(types.Float), idx, b)
case types.TimestampKind:
translateTimestampField(value.(types.Timestamp), idx, b)
case types.BoolKind:
b.PutBool(idx, bool(value.(types.Bool)))
case types.StringKind:
return translateStringField(ctx, ns, value.(types.String), idx, b)
case types.UUIDKind:
uuid := value.(types.UUID)
b.PutHash128(idx, uuid[:])
case types.InlineBlobKind:
b.PutByteString(idx, value.(types.InlineBlob))
case types.DecimalKind:
b.PutDecimal(idx, decimal.Decimal(value.(types.Decimal)))
case types.GeometryKind:
v := value.(types.Geometry).Inner
translateGeometryField(v, idx, b)
case types.PointKind,
types.LineStringKind,
types.PolygonKind,
types.MultiPointKind,
types.MultiLineStringKind,
types.MultiPolygonKind,
types.GeometryCollectionKind:
translateGeometryField(value, idx, b)
case types.JSONKind:
return translateJSONField(ctx, ns, value.(types.JSON), idx, b)
case types.BlobKind:
return translateBlobField(ctx, ns, value.(types.Blob), idx, b)
case types.ExtendedKind:
return fmt.Errorf("extended types are invalid during migration")
default:
return fmt.Errorf("encountered unexpected NomsKind %s",
types.KindToString[nk])
}
return nil
}
func translateUintField(value types.Uint, idx int, b *val.TupleBuilder) {
typ := b.Desc.Types[idx]
switch typ.Enc {
case val.Uint8Enc:
b.PutUint8(idx, uint8(value))
case val.Uint16Enc:
b.PutUint16(idx, uint16(value))
case val.Uint32Enc:
b.PutUint32(idx, uint32(value))
case val.Uint64Enc:
b.PutUint64(idx, uint64(value))
case val.EnumEnc:
b.PutEnum(idx, uint16(value))
case val.SetEnc:
b.PutSet(idx, uint64(value))
default:
panic(fmt.Sprintf("unexpected encoding for uint (%d)", typ.Enc))
}
}
func translateIntField(value types.Int, idx int, b *val.TupleBuilder) {
typ := b.Desc.Types[idx]
switch typ.Enc {
case val.Int8Enc:
b.PutInt8(idx, int8(value))
case val.Int16Enc:
b.PutInt16(idx, int16(value))
case val.Int32Enc:
b.PutInt32(idx, int32(value))
case val.Int64Enc:
b.PutInt64(idx, int64(value))
case val.YearEnc:
b.PutYear(idx, int16(value))
case val.TimeEnc:
b.PutSqlTime(idx, int64(value))
default:
panic(fmt.Sprintf("unexpected encoding for int (%d)", typ.Enc))
}
}
func translateFloatField(value types.Float, idx int, b *val.TupleBuilder) {
typ := b.Desc.Types[idx]
switch typ.Enc {
case val.Float32Enc:
b.PutFloat32(idx, float32(value))
case val.Float64Enc:
b.PutFloat64(idx, float64(value))
default:
panic(fmt.Sprintf("unexpected encoding for float (%d)", typ.Enc))
}
}
func translateStringField(ctx context.Context, ns tree.NodeStore, value types.String, idx int, b *val.TupleBuilder) error {
typ := b.Desc.Types[idx]
switch typ.Enc {
case val.StringEnc:
b.PutString(idx, string(value))
case val.StringAddrEnc:
// note: previously, TEXT fields were serialized as types.String
rd := strings.NewReader(string(value))
bb := ns.BlobBuilder()
defer ns.PutBlobBuilder(bb)
bb.Init(len(value))
_, addr, err := bb.Chunk(ctx, rd)
if err != nil {
return err
}
b.PutStringAddr(idx, addr)
default:
panic(fmt.Sprintf("unexpected encoding for string (%d)", typ.Enc))
}
return nil
}
func translateTimestampField(value types.Timestamp, idx int, b *val.TupleBuilder) {
typ := b.Desc.Types[idx]
switch typ.Enc {
case val.DateEnc:
b.PutDate(idx, time.Time(value))
case val.DatetimeEnc:
b.PutDatetime(idx, time.Time(value))
default:
panic(fmt.Sprintf("unexpected encoding for timestamp (%d)", typ.Enc))
}
}
func translateGeometryField(value types.Value, idx int, b *val.TupleBuilder) {
nk := value.Kind()
switch nk {
case types.PointKind:
p := types.ConvertTypesPointToSQLPoint(value.(types.Point))
b.PutGeometry(idx, p.Serialize())
case types.LineStringKind:
l := types.ConvertTypesLineStringToSQLLineString(value.(types.LineString))
b.PutGeometry(idx, l.Serialize())
case types.PolygonKind:
p := types.ConvertTypesPolygonToSQLPolygon(value.(types.Polygon))
b.PutGeometry(idx, p.Serialize())
case types.MultiPointKind:
p := types.ConvertTypesMultiPointToSQLMultiPoint(value.(types.MultiPoint))
b.PutGeometry(idx, p.Serialize())
case types.MultiLineStringKind:
l := types.ConvertTypesMultiLineStringToSQLMultiLineString(value.(types.MultiLineString))
b.PutGeometry(idx, l.Serialize())
case types.MultiPolygonKind:
p := types.ConvertTypesMultiPolygonToSQLMultiPolygon(value.(types.MultiPolygon))
b.PutGeometry(idx, p.Serialize())
case types.GeometryCollectionKind:
p := types.ConvertTypesGeomCollToSQLGeomColl(value.(types.GeomColl))
b.PutGeometry(idx, p.Serialize())
default:
panic(fmt.Sprintf("unexpected NomsKind for geometry (%d)", nk))
}
}
func translateJSONField(ctx context.Context, ns tree.NodeStore, value types.JSON, idx int, b *val.TupleBuilder) error {
s, err := json.NomsJSONToString(ctx, json.NomsJSON(value))
if err != nil {
return err
}
buf := bytes.NewBuffer([]byte(s))
bb := ns.BlobBuilder()
defer ns.PutBlobBuilder(bb)
bb.Init(len(s))
_, addr, err := bb.Chunk(ctx, buf)
if err != nil {
return err
}
b.PutJSONAddr(idx, addr)
return nil
}
func translateBlobField(ctx context.Context, ns tree.NodeStore, value types.Blob, idx int, b *val.TupleBuilder) error {
switch b.Desc.Types[idx].Enc {
// maybe convert from TEXT/BLOB to VARBINARY/VARCHAR
// if this column is a primary/secondary index key
case val.StringEnc, val.ByteStringEnc:
return translateBlobValueToInlineField(ctx, value, idx, b)
case val.StringAddrEnc, val.BytesAddrEnc:
// common case
default:
return fmt.Errorf("unexpected encoding for blob (%d)", b.Desc.Types[idx].Enc)
}
buf := make([]byte, value.Len())
_, err := value.ReadAt(ctx, buf, 0)
if err == io.EOF {
err = nil
} else if err != nil {
return err
}
bb := ns.BlobBuilder()
defer ns.PutBlobBuilder(bb)
bb.Init(int(value.Len()))
_, addr, err := bb.Chunk(ctx, bytes.NewReader(buf))
if err != nil {
return err
}
typ := b.Desc.Types[idx]
switch typ.Enc {
case val.BytesAddrEnc:
b.PutBytesAddr(idx, addr)
case val.StringAddrEnc:
b.PutStringAddr(idx, addr)
}
return nil
}
func translateBlobValueToInlineField(ctx context.Context, value types.Blob, idx int, b *val.TupleBuilder) error {
if value.Len() >= maxInlineValue {
if b.Desc.Types[idx].Enc == val.StringEnc {
return ErrCannotMigrateText
} else {
return ErrCannotMigrateBlob
}
}
buf := make([]byte, value.Len())
_, err := value.ReadAt(ctx, buf, 0)
if err == io.EOF {
err = nil
} else if err != nil {
return err
}
typ := b.Desc.Types[idx]
switch typ.Enc {
case val.ByteStringEnc:
b.PutByteString(idx, buf)
case val.StringEnc:
b.PutString(idx, string(buf))
default:
panic(fmt.Sprintf("unexpected encoding for blob (%d)", typ.Enc))
}
return nil
}
func isEven(n uint64) bool {
return n%2 == 0
}
-345
View File
@@ -1,345 +0,0 @@
// Copyright 2022 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 migrate
import (
"context"
"fmt"
"io"
"runtime"
"strings"
"time"
"unicode"
"github.com/dolthub/go-mysql-server/sql"
gmstypes "github.com/dolthub/go-mysql-server/sql/types"
"github.com/dolthub/vitess/go/vt/proto/query"
"golang.org/x/sync/errgroup"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle"
"github.com/dolthub/dolt/go/store/types"
)
func validateBranchMapping(ctx context.Context, old, new *doltdb.DoltDB) error {
branches, err := old.GetBranches(ctx)
if err != nil {
return err
}
var ok bool
for _, bref := range branches {
_, ok, err = new.HasBranch(ctx, bref.GetPath())
if err != nil {
return err
}
if !ok {
return fmt.Errorf("failed to map branch %s", bref.GetPath())
}
}
return nil
}
func validateRootValue(ctx context.Context, oldParent, old, new doltdb.RootValue) error {
names, err := old.GetTableNames(ctx, doltdb.DefaultSchemaName, true)
if err != nil {
return err
}
for _, name := range names {
o, ok, err := old.GetTable(ctx, doltdb.TableName{Name: name})
if err != nil {
return err
}
if !ok {
h, _ := old.HashOf()
return fmt.Errorf("expected to find table %s in root value (%s)", name, h.String())
}
// Skip tables that haven't changed
op, ok, err := oldParent.GetTable(ctx, doltdb.TableName{Name: name})
if err != nil {
return err
}
if ok {
oldHash, err := o.HashOf()
if err != nil {
return err
}
oldParentHash, err := op.HashOf()
if err != nil {
return err
}
if oldHash.Equal(oldParentHash) {
continue
}
}
n, ok, err := new.GetTable(ctx, doltdb.TableName{Name: name})
if err != nil {
return err
}
if !ok {
h, _ := new.HashOf()
return fmt.Errorf("expected to find table %s in root value (%s)", name, h.String())
}
if err = validateTableData(ctx, name, o, n); err != nil {
return err
}
}
return nil
}
func validateTableData(ctx context.Context, name string, old, new *doltdb.Table) error {
parts, err := partitionTable(ctx, old)
if err != nil {
return err
} else if len(parts) == 0 {
return nil
}
eg, ctx := errgroup.WithContext(ctx)
for i := range parts {
start, end := parts[i][0], parts[i][1]
eg.Go(func() error {
return validateTableDataPartition(ctx, name, old, new, start, end)
})
}
return eg.Wait()
}
func validateTableDataPartition(ctx context.Context, name string, old, new *doltdb.Table, start, end uint64) error {
sctx := sql.NewContext(ctx)
_, oldIter, err := sqle.DoltTablePartitionToRowIter(sctx, name, old, start, end)
if err != nil {
return err
}
newSch, newIter, err := sqle.DoltTablePartitionToRowIter(sctx, name, new, start, end)
if err != nil {
return err
}
var o, n sql.Row
for {
o, err = oldIter.Next(sctx)
if err == io.EOF {
break
} else if err != nil {
return err
}
n, err = newIter.Next(sctx)
if err != nil {
return err
}
ok, err := equalRows(ctx, o, n, newSch)
if err != nil {
return err
} else if !ok {
return fmt.Errorf("differing rows for table %s (%s != %s)",
name, sql.FormatRow(o), sql.FormatRow(n))
}
}
// validated that newIter is also exhausted
_, err = newIter.Next(sctx)
if err != io.EOF {
return fmt.Errorf("differing number of rows for table %s", name)
}
return nil
}
func equalRows(ctx context.Context, old, new sql.Row, sch sql.Schema) (bool, error) {
if len(new) != len(old) || len(new) != len(sch) {
return false, nil
}
var cmp int
for i := range new {
// special case string comparisons
s, ok, err := sql.Unwrap[string](ctx, old[i])
if err != nil {
return false, err
}
if ok {
old[i] = strings.TrimRightFunc(s, unicode.IsSpace)
}
s, ok, err = sql.Unwrap[string](ctx, new[i])
if err != nil {
return false, err
}
if ok {
new[i] = strings.TrimRightFunc(s, unicode.IsSpace)
}
// special case time comparison to account
// for precision changes between formats
if _, ok := old[i].(time.Time); ok {
var o, n interface{}
if o, _, err = gmstypes.Int64.Convert(ctx, old[i]); err != nil {
return false, err
}
if n, _, err = gmstypes.Int64.Convert(ctx, new[i]); err != nil {
return false, err
}
if cmp, err = gmstypes.Int64.Compare(ctx, o, n); err != nil {
return false, err
}
} else {
if cmp, err = sch[i].Type.Compare(ctx, old[i], new[i]); err != nil {
return false, err
}
}
if cmp != 0 {
return false, nil
}
}
return true, nil
}
func validateSchema(existing schema.Schema) error {
for _, c := range existing.GetAllCols().GetColumns() {
qt := c.TypeInfo.ToSqlType().Type()
err := assertNomsKind(c.Kind, nomsKindsFromQueryTypes(qt)...)
if err != nil {
return err
}
}
return nil
}
func nomsKindsFromQueryTypes(qt query.Type) []types.NomsKind {
switch qt {
case query.Type_UINT8:
return []types.NomsKind{types.UintKind, types.BoolKind}
case query.Type_UINT16, query.Type_UINT24,
query.Type_UINT32, query.Type_UINT64:
return []types.NomsKind{types.UintKind}
case query.Type_INT8:
return []types.NomsKind{types.IntKind, types.BoolKind}
case query.Type_INT16, query.Type_INT24,
query.Type_INT32, query.Type_INT64:
return []types.NomsKind{types.IntKind}
case query.Type_YEAR, query.Type_TIME:
return []types.NomsKind{types.IntKind}
case query.Type_FLOAT32, query.Type_FLOAT64:
return []types.NomsKind{types.FloatKind}
case query.Type_TIMESTAMP, query.Type_DATE, query.Type_DATETIME:
return []types.NomsKind{types.TimestampKind}
case query.Type_DECIMAL:
return []types.NomsKind{types.DecimalKind}
case query.Type_TEXT, query.Type_BLOB:
return []types.NomsKind{
types.BlobKind,
types.StringKind,
}
case query.Type_VARCHAR, query.Type_CHAR:
return []types.NomsKind{types.StringKind}
case query.Type_VARBINARY, query.Type_BINARY:
return []types.NomsKind{types.InlineBlobKind}
case query.Type_BIT, query.Type_ENUM, query.Type_SET:
return []types.NomsKind{types.UintKind}
case query.Type_GEOMETRY:
return []types.NomsKind{
types.GeometryKind,
types.PointKind,
types.LineStringKind,
types.PolygonKind,
types.MultiPointKind,
types.MultiLineStringKind,
types.MultiPolygonKind,
types.GeometryCollectionKind,
}
case query.Type_JSON:
return []types.NomsKind{types.JSONKind}
default:
panic(fmt.Sprintf("unexpect query.Type %s", qt.String()))
}
}
func assertNomsKind(kind types.NomsKind, candidates ...types.NomsKind) error {
for _, c := range candidates {
if kind == c {
return nil
}
}
cs := make([]string, len(candidates))
for i, c := range candidates {
cs[i] = types.KindToString[c]
}
return fmt.Errorf("expected NomsKind to be one of (%s), got NomsKind (%s)",
strings.Join(cs, ", "), types.KindToString[kind])
}
func partitionTable(ctx context.Context, tbl *doltdb.Table) ([][2]uint64, error) {
idx, err := tbl.GetRowData(ctx)
if err != nil {
return nil, err
}
c, err := idx.Count()
if err != nil {
return nil, err
}
if c == 0 {
return nil, nil
}
n := runtime.NumCPU() * 2
szc, err := idx.Count()
if err != nil {
return nil, err
}
sz := int(szc) / n
parts := make([][2]uint64, n)
parts[0][0] = 0
parts[n-1][1], err = idx.Count()
if err != nil {
return nil, err
}
for i := 1; i < len(parts); i++ {
parts[i-1][1] = uint64(i * sz)
parts[i][0] = uint64(i * sz)
}
return parts, nil
}
func assertTrue(b bool) {
if !b {
panic("expected true")
}
}
-181
View File
@@ -1,181 +0,0 @@
// Copyright 2019 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 rowconv
import (
"errors"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/store/types"
)
// ColNamingFunc defines a function signature which takes the name of a column, and returns the name that should be used
// for the column in the joined dataset.
type ColNamingFunc func(colName string) string
type stringUint64Tuple struct {
str string
u64 uint64
}
// NamedSchema is an object that associates a schema with a string
type NamedSchema struct {
// Name the name given to the schema
Name string
// Sch is the schema
Sch schema.Schema
}
// Joiner is an object that can be used to join multiple rows together into a single row (See Join), and also to reverse
// this operation by taking a joined row and getting back a map of rows (See Split).
type Joiner struct {
srcSchemas map[string]schema.Schema
tagMaps map[string]map[uint64]uint64
revTagMap map[uint64]stringUint64Tuple
joined schema.Schema
}
// NewJoiner creates a joiner from a slice of NamedSchemas and a map of ColNamingFuncs. A new schema for joined rows will
// be created, and the columns for joined schemas will be named according to the ColNamingFunc associated with each schema
// name.
func NewJoiner(namedSchemas []NamedSchema, namers map[string]ColNamingFunc) (*Joiner, error) {
tags := make(map[string][]uint64)
revTagMap := make(map[uint64]stringUint64Tuple)
tagMaps := make(map[string]map[uint64]uint64, len(namedSchemas))
srcSchemas := make(map[string]schema.Schema)
for _, namedSch := range namedSchemas {
tagMaps[namedSch.Name] = make(map[uint64]uint64)
srcSchemas[namedSch.Name] = namedSch.Sch
}
var cols []schema.Column
var destTag uint64
var err error
for _, namedSch := range namedSchemas {
sch := namedSch.Sch
name := namedSch.Name
allCols := sch.GetAllCols()
namer := namers[name]
allCols.IterInSortedOrder(func(srcTag uint64, col schema.Column) (stop bool) {
newColName := namer(col.Name)
var newCol schema.Column
newCol, err = schema.NewColumnWithTypeInfo(newColName, destTag, col.TypeInfo, false, col.Default, false, col.Comment)
if err != nil {
return true
}
cols = append(cols, newCol)
tagMaps[name][srcTag] = destTag
revTagMap[destTag] = stringUint64Tuple{str: name, u64: srcTag}
tags[name] = append(tags[name], destTag)
destTag++
return false
})
}
if err != nil {
return nil, err
}
colColl := schema.NewColCollection(cols...)
joined := schema.UnkeyedSchemaFromCols(colColl)
return &Joiner{srcSchemas, tagMaps, revTagMap, joined}, nil
}
// Join takes a map from schema name to row which has that schema, and returns a single joined row containing all the
// data
func (j *Joiner) Join(namedRows map[string]row.Row) (row.Row, error) {
var nbf *types.NomsBinFormat
colVals := make(row.TaggedValues)
for name, r := range namedRows {
if r == nil {
continue
}
if nbf == nil {
nbf = r.Format()
} else if nbf.VersionString() != r.Format().VersionString() {
return nil, errors.New("not all rows have the same format")
}
_, err := r.IterCols(func(tag uint64, val types.Value) (stop bool, err error) {
destTag := j.tagMaps[name][tag]
colVals[destTag] = val
return false, nil
})
if err != nil {
return nil, err
}
}
return row.New(nbf, j.joined, colVals)
}
// Split takes a row which has the created joined schema, and splits it into a map of rows where the key of the map is
// the name of the schema for the associated row.
func (j *Joiner) Split(r row.Row) (map[string]row.Row, error) {
colVals := make(map[string]row.TaggedValues, len(j.srcSchemas))
for name := range j.srcSchemas {
colVals[name] = make(row.TaggedValues)
}
_, err := r.IterCols(func(tag uint64, val types.Value) (stop bool, err error) {
schemaNameAndTag, ok := j.revTagMap[tag]
if !ok {
return false, nil
}
colVals[schemaNameAndTag.str][schemaNameAndTag.u64] = val
return false, nil
})
if err != nil {
return nil, err
}
rows := make(map[string]row.Row, len(colVals))
for name, sch := range j.srcSchemas {
var err error
currColVals := colVals[name]
if len(currColVals) == 0 {
continue
}
rows[name], err = row.New(r.Format(), sch, currColVals)
if err != nil {
return nil, err
}
}
return rows, nil
}
// GetSchema returns the schema which all joined rows will have, and any row passed into split should have.
func (j *Joiner) GetSchema() schema.Schema {
return j.joined
}
// SchemaForName retrieves the original schema which has the given name.
func (j *Joiner) SchemaForName(name string) schema.Schema {
return j.srcSchemas[name]
}
@@ -1,187 +0,0 @@
// Copyright 2019 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 rowconv
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/store/types"
)
const (
firstTag uint64 = iota
lastTag
ageTag
cityTag
)
var peopleCols = schema.NewColCollection(
schema.NewColumn("last", lastTag, types.StringKind, true),
schema.NewColumn("first", firstTag, types.StringKind, true),
schema.NewColumn("age", ageTag, types.IntKind, false),
schema.NewColumn("city", cityTag, types.StringKind, false),
)
var peopleSch = schema.MustSchemaFromCols(peopleCols)
type toJoinAndExpectedResult struct {
toJoinVals map[string]row.TaggedValues
expected map[string]types.Value
}
func TestJoiner(t *testing.T) {
tests := []struct {
name string
namedSchemas []NamedSchema
namers map[string]ColNamingFunc
toJoin []toJoinAndExpectedResult
}{
{
name: "join diff versions of row",
namedSchemas: []NamedSchema{{"to", peopleSch}, {"from", peopleSch}},
namers: map[string]ColNamingFunc{"to": toNamer, "from": fromNamer},
toJoin: []toJoinAndExpectedResult{
{
toJoinVals: map[string]row.TaggedValues{
"from": {
lastTag: types.String("Richardson"),
firstTag: types.String("Richard"),
ageTag: types.Int(42),
cityTag: types.String("San Francisco"),
},
"to": {
lastTag: types.String("Richardson"),
firstTag: types.String("Richard"),
ageTag: types.Int(43),
cityTag: types.String("Los Angeles"),
},
},
expected: map[string]types.Value{
"from_last": types.String("Richardson"),
"from_first": types.String("Richard"),
"from_city": types.String("San Francisco"),
"from_age": types.Int(42),
"to_last": types.String("Richardson"),
"to_first": types.String("Richard"),
"to_city": types.String("Los Angeles"),
"to_age": types.Int(43),
},
},
{
toJoinVals: map[string]row.TaggedValues{
"from": {
lastTag: types.String("Richardson"),
firstTag: types.String("Richard"),
ageTag: types.Int(42),
cityTag: types.String("San Francisco"),
},
},
expected: map[string]types.Value{
"from_last": types.String("Richardson"),
"from_first": types.String("Richard"),
"from_city": types.String("San Francisco"),
"from_age": types.Int(42),
},
},
{
toJoinVals: map[string]row.TaggedValues{
"to": {
lastTag: types.String("Richardson"),
firstTag: types.String("Richard"),
ageTag: types.Int(43),
cityTag: types.String("Los Angeles"),
},
},
expected: map[string]types.Value{
"to_last": types.String("Richardson"),
"to_first": types.String("Richard"),
"to_city": types.String("Los Angeles"),
"to_age": types.Int(43),
},
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
j, err := NewJoiner(test.namedSchemas, test.namers)
require.NoError(t, err)
for _, tj := range test.toJoin {
rows := map[string]row.Row{}
for _, namedSch := range test.namedSchemas {
r, err := row.New(types.Format_Default, namedSch.Sch, tj.toJoinVals[namedSch.Name])
require.NoError(t, err)
rows[namedSch.Name] = r
}
joinedRow, err := j.Join(rows)
require.NoError(t, err)
joinedSch := j.GetSchema()
_, err = joinedRow.IterCols(func(tag uint64, val types.Value) (stop bool, err error) {
col, ok := joinedSch.GetAllCols().GetByTag(tag)
assert.True(t, ok)
expectedVal := tj.expected[col.Name]
assert.Equal(t, val, expectedVal)
return false, nil
})
require.NoError(t, err)
splitRows, err := j.Split(joinedRow)
require.NoError(t, err)
assert.Equal(t, len(tj.toJoinVals), len(splitRows))
for _, namedSch := range test.namedSchemas {
name := namedSch.Name
sch := namedSch.Sch
actual := splitRows[name]
expectedVals := tj.toJoinVals[name]
if actual == nil && expectedVals == nil {
continue
}
assert.False(t, actual == nil || expectedVals == nil)
expected, err := row.New(types.Format_Default, sch, expectedVals)
require.NoError(t, err)
assert.True(t, row.AreEqual(actual, expected, sch))
}
}
})
}
}
func fromNamer(name string) string {
return "from_" + name
}
func toNamer(name string) string {
return "to_" + name
}
+2 -2
View File
@@ -265,7 +265,7 @@ func GetSharedCols(schema Schema, cmpNames []string, cmpKinds []types.NomsKind)
// ArePrimaryKeySetsDiffable checks if two schemas are diffable. Assumes the
// passed in schema are from the same table between commits. If __DOLT__, then
// it also checks if the underlying SQL types of the columns are equal.
func ArePrimaryKeySetsDiffable(format *types.NomsBinFormat, fromSch, toSch Schema) bool {
func ArePrimaryKeySetsDiffable(fromSch, toSch Schema) bool {
if fromSch == nil && toSch == nil {
return false
// Empty case
@@ -292,7 +292,7 @@ func ArePrimaryKeySetsDiffable(format *types.NomsBinFormat, fromSch, toSch Schem
if (c1.Tag != c2.Tag) || (c1.IsPartOfPK != c2.IsPartOfPK) {
return false
}
if types.IsFormat_DOLT(format) && !c1.TypeInfo.ToSqlType().Equals(c2.TypeInfo.ToSqlType()) {
if !c1.TypeInfo.ToSqlType().Equals(c2.TypeInfo.ToSqlType()) {
return false
}
}
+2 -13
View File
@@ -339,7 +339,7 @@ func TestArePrimaryKeySetsDiffable(t *testing.T) {
for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
d := ArePrimaryKeySetsDiffable(types.Format_Default, test.From, test.To)
d := ArePrimaryKeySetsDiffable(test.From, test.To)
require.Equal(t, test.Diffable, d)
// If they are diffable then we should be able to map their schemas from one to another.
@@ -359,7 +359,6 @@ func TestArePrimaryKeySetsDiffableTypeChanges(t *testing.T) {
From Schema
To Schema
Diffable bool
Format *types.NomsBinFormat
}{
{
Name: "Int -> String (New Format)",
@@ -368,22 +367,12 @@ func TestArePrimaryKeySetsDiffableTypeChanges(t *testing.T) {
To: MustSchemaFromCols(NewColCollection(
NewColumn("pk", 0, types.StringKind, true))),
Diffable: false,
Format: types.Format_DOLT,
},
{
Name: "Int -> String (Old Format)",
From: MustSchemaFromCols(NewColCollection(
NewColumn("pk", 0, types.IntKind, true))),
To: MustSchemaFromCols(NewColCollection(
NewColumn("pk", 0, types.StringKind, true))),
Diffable: true,
Format: types.Format_LD_1,
},
}
for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
d := ArePrimaryKeySetsDiffable(test.Format, test.From, test.To)
d := ArePrimaryKeySetsDiffable(test.From, test.To)
require.Equal(t, test.Diffable, d)
})
}
@@ -31,7 +31,6 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/utils/filesys"
"github.com/dolthub/dolt/go/libraries/utils/test"
@@ -381,30 +380,8 @@ func createTestRowData(t *testing.T, vrw types.ValueReadWriter, ns tree.NodeStor
return idx
}
vals := []row.TaggedValues{
{idTag: types.UUID(id0), firstTag: types.String("bill"), lastTag: types.String("billerson"), ageTag: types.Uint(53)},
{idTag: types.UUID(id1), firstTag: types.String("eric"), lastTag: types.String("ericson"), isMarriedTag: types.Bool(true), ageTag: types.Uint(21)},
{idTag: types.UUID(id2), firstTag: types.String("john"), lastTag: types.String("johnson"), isMarriedTag: types.Bool(false), ageTag: types.Uint(53)},
{idTag: types.UUID(id3), firstTag: types.String("robert"), lastTag: types.String("robertson"), ageTag: types.Uint(36)},
}
var err error
rows := make([]row.Row, len(vals))
m, err := types.NewMap(context.Background(), vrw)
assert.NoError(t, err)
ed := m.Edit()
for i, val := range vals {
r, err := row.New(vrw.Format(), sch, val)
require.NoError(t, err)
rows[i] = r
ed = ed.Set(r.NomsMapKey(sch), r.NomsMapValue(sch))
}
m, err = ed.Map(context.Background())
assert.NoError(t, err)
return durable.IndexFromNomsMap(m, vrw, ns)
t.Fatal("unsupported format")
return nil
}
func createHooksTestTable(vrw types.ValueReadWriter, ns tree.NodeStore, tSchema schema.Schema, rowData durable.Index) (*doltdb.Table, error) {
@@ -35,7 +35,6 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess/mutexmap"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/globalstate"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/types"
)
type LockMode int64
@@ -374,41 +373,36 @@ func (a *AutoIncrementTracker) deepSet(ctx *sql.Context, tableName string, table
}
func getMaxIndexValue(ctx *sql.Context, indexData durable.Index) (uint64, error) {
if types.IsFormat_DOLT(indexData.Format()) {
idx, err := durable.ProllyMapFromIndex(indexData)
if err != nil {
return 0, err
}
iter, err := idx.IterAllReverse(ctx)
if err != nil {
return 0, err
}
kd, _ := idx.Descriptors()
k, _, err := iter.Next(ctx)
if err == io.EOF {
return 0, nil
} else if err != nil {
return 0, err
}
// TODO: is the auto-inc column always the first column in the index?
field, err := tree.GetField(ctx, kd, 0, k, idx.NodeStore())
if err != nil {
return 0, err
}
maxVal, err := CoerceAutoIncrementValue(ctx, field)
if err != nil {
return 0, err
}
return maxVal, nil
idx, err := durable.ProllyMapFromIndex(indexData)
if err != nil {
return 0, err
}
// For an LD format table, this operation won't succeed
return math.MaxUint64, nil
iter, err := idx.IterAllReverse(ctx)
if err != nil {
return 0, err
}
kd, _ := idx.Descriptors()
k, _, err := iter.Next(ctx)
if err == io.EOF {
return 0, nil
} else if err != nil {
return 0, err
}
// TODO: is the auto-inc column always the first column in the index?
field, err := tree.GetField(ctx, kd, 0, k, idx.NodeStore())
if err != nil {
return 0, err
}
maxVal, err := CoerceAutoIncrementValue(ctx, field)
if err != nil {
return 0, err
}
return maxVal, nil
}
// AddNewTable initializes a new table with an auto increment column to the tracker, as necessary
@@ -29,7 +29,6 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/merge"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/doltcore/rowconv"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables"
@@ -57,7 +56,6 @@ type DiffTableFunction struct {
database sql.Database
overriddenSchema schema.Schema
ctx *sql.Context
joiner *rowconv.Joiner
fromDate *types.Timestamp
toDate *types.Timestamp
includeCols map[string]struct{}
@@ -233,7 +231,7 @@ func (dtf *DiffTableFunction) RowIter(ctx *sql.Context, _ sql.Row) (sql.RowIter,
dp := dtables.NewDiffPartition(dtf.tableDelta.ToTable, dtf.tableDelta.FromTable, toCommitStr, fromCommitStr, dtf.toDate, dtf.fromDate, toSchForPartition, fromSchForPartition, nil)
return dtables.NewDiffPartitionRowIter(dp, ddb, dtf.joiner), nil
return dtables.NewDiffPartitionRowIter(dp, ddb), nil
}
// findMatchingDelta returns the best matching table delta for the table name
@@ -678,11 +676,10 @@ func (dtf *DiffTableFunction) generateSchema(ctx *sql.Context, fromCommitVal, to
toSchForJoiner = dtf.overriddenSchema
}
diffTableSch, j, err := dtables.GetDiffTableSchemaAndJoiner(format, fromSchForJoiner, toSchForJoiner)
diffTableSch, err := dtables.GetDiffTableSchemaAndJoiner(format, fromSchForJoiner, toSchForJoiner)
if err != nil {
return err
}
dtf.joiner = j
// TODO: sql.Columns include a Source that indicates the table it came from, but we don't have a real table
// when the column comes from a table function, so we omit the table name when we create these columns.
@@ -352,7 +352,7 @@ func getSummaryForDelta(ctx *sql.Context, delta diff.TableDelta, sqledb dsess.Sq
return summ, nil
}
if !schema.ArePrimaryKeySetsDiffable(delta.Format(), delta.FromSch, delta.ToSch) {
if !schema.ArePrimaryKeySetsDiffable(delta.FromSch, delta.ToSch) {
if shouldErrorOnPKChange {
return nil, fmt.Errorf("failed to compute diff summary for table %s: %w", delta.CurName(), diff.ErrPrimaryKeySetChanged)
}
@@ -408,9 +408,9 @@ func (ds *DiffSummaryTableFunction) evaluateArguments() (interface{}, interface{
return fromCommitVal, toCommitVal, nil, tableName, nil
}
//------------------------------------
// ------------------------------------
// diffSummaryTableFunctionRowIter
//------------------------------------
// ------------------------------------
var _ sql.RowIter = &diffSummaryTableFunctionRowIter{}
@@ -547,7 +547,7 @@ func canGetDataDiff(ctx *sql.Context, td diff.TableDelta) bool {
}
// not diffable
if !schema.ArePrimaryKeySetsDiffable(td.Format(), td.FromSch, td.ToSch) {
if !schema.ArePrimaryKeySetsDiffable(td.FromSch, td.ToSch) {
ctx.Session.Warn(&sql.Warning{
Level: "Warning",
Code: mysql.ERNotSupportedYet,
@@ -625,7 +625,7 @@ func getDataSqlPatchResults(ctx *sql.Context, diffQuerySch, targetSch sql.Schema
// fmt.Sprintf("select %s, %s from dolt_diff('%s', '%s', '%s')", columnsWithDiff, "diff_type", fromRef, toRef, tableName)
// on sql engine, which returns the schema and rowIter of the final data diff result.
func getDiffQuery(ctx *sql.Context, dbData env.DbData[*sql.Context], td diff.TableDelta, fromRefDetails, toRefDetails *refDetails) (sql.Schema, []sql.Expression, sql.RowIter, error) {
diffTableSchema, j, err := dtables.GetDiffTableSchemaAndJoiner(td.ToTable.Format(), td.FromSch, td.ToSch)
diffTableSchema, err := dtables.GetDiffTableSchemaAndJoiner(td.ToTable.Format(), td.FromSch, td.ToSch)
if err != nil {
return nil, nil, nil, err
}
@@ -638,7 +638,7 @@ func getDiffQuery(ctx *sql.Context, dbData env.DbData[*sql.Context], td diff.Tab
diffQuerySqlSch, projections := getDiffQuerySqlSchemaAndProjections(diffPKSch.Schema, columnsWithDiff)
dp := dtables.NewDiffPartition(td.ToTable, td.FromTable, toRefDetails.hashStr, fromRefDetails.hashStr, toRefDetails.commitTime, fromRefDetails.commitTime, td.ToSch, td.FromSch, nil)
ri := dtables.NewDiffPartitionRowIter(dp, dbData.Ddb, j)
ri := dtables.NewDiffPartitionRowIter(dp, dbData.Ddb)
return diffQuerySqlSch, projections, ri, nil
}
@@ -693,9 +693,9 @@ func getDiffQuerySqlSchemaAndProjections(diffTableSch sql.Schema, columns []stri
return cols, getFieldCols
}
//------------------------------------
// ------------------------------------
// patchTableFunctionRowIter
//------------------------------------
// ------------------------------------
var _ sql.RowIter = (*patchTableFunctionRowIter)(nil)
@@ -761,9 +761,9 @@ func (itr *patchTableFunctionRowIter) Close(_ *sql.Context) error {
return nil
}
//------------------------------------
// ------------------------------------
// patchStatementsRowIter
//------------------------------------
// ------------------------------------
var _ sql.RowIter = (*patchStatementsRowIter)(nil)
@@ -574,9 +574,9 @@ func (pm *PreviewMergeConflictsTableFunction) evaluateArguments() (interface{},
return leftBranchVal, rightBranchVal, tableName, nil
}
//--------------------------------------------------
// --------------------------------------------------
// previewMergeConflictsTableFunctionRowIter
//--------------------------------------------------
// --------------------------------------------------
var _ sql.RowIter = &previewMergeConflictsTableFunctionRowIter{}
@@ -519,7 +519,7 @@ func processTableColDelta(ctx *sql.Context, ddb *doltdb.DoltDB, delta diff.Table
// cells to compile a list of modified columns
func calculateColDelta(ctx *sql.Context, ddb *doltdb.DoltDB, delta *diff.TableDelta, colSchDiff *colSchemaDiff) ([]string, []string, error) {
// initialize row iterator
diffTableSchema, j, err := GetDiffTableSchemaAndJoiner(delta.ToTable.Format(), delta.FromSch, delta.ToSch)
diffTableSchema, err := GetDiffTableSchemaAndJoiner(delta.ToTable.Format(), delta.FromSch, delta.ToSch)
if err != nil {
return nil, nil, err
}
@@ -528,7 +528,7 @@ func calculateColDelta(ctx *sql.Context, ddb *doltdb.DoltDB, delta *diff.TableDe
now := time.Now() // accurate commit time returned elsewhere
// TODO: schema name?
dp := NewDiffPartition(delta.ToTable, delta.FromTable, delta.ToName.Name, delta.FromName.Name, (*dtypes.Timestamp)(&now), (*dtypes.Timestamp)(&now), delta.ToSch, delta.FromSch, nil)
ri := NewDiffPartitionRowIter(dp, ddb, j)
ri := NewDiffPartitionRowIter(dp, ddb)
var resultColNames []string
var resultDiffTypes []string
@@ -25,7 +25,6 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/doltcore/rowconv"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
@@ -45,7 +44,6 @@ type CommitDiffTable struct {
requiredFilterErr error
ddb *doltdb.DoltDB
table *doltdb.Table
joiner *rowconv.Joiner
tableName doltdb.TableName
targetSchema schema.Schema
@@ -76,7 +74,7 @@ func NewCommitDiffTable(ctx *sql.Context, dbName string, tblName doltdb.TableNam
return nil, err
}
diffTableSchema, j, err := GetDiffTableSchemaAndJoiner(ddb.Format(), sch, sch)
diffTableSchema, err := GetDiffTableSchemaAndJoiner(ddb.Format(), sch, sch)
if err != nil {
return nil, err
}
@@ -94,7 +92,6 @@ func NewCommitDiffTable(ctx *sql.Context, dbName string, tblName doltdb.TableNam
workingRoot: wRoot,
stagedRoot: sRoot,
headRef: headRef,
joiner: j,
sqlSch: sqlSch,
targetSchema: sch,
}, nil
@@ -320,5 +317,5 @@ func (dt *CommitDiffTable) rootValForHash(ctx *sql.Context, hashStr string) (dol
func (dt *CommitDiffTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) {
dp := part.(DiffPartition)
return dp.GetRowIter(ctx, dt.ddb, dt.joiner)
return dp.GetRowIter(ctx)
}
@@ -15,15 +15,11 @@ package dtables
// limitations under the License.
import (
"errors"
"fmt"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/merge"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
"github.com/dolthub/dolt/go/store/types"
)
@@ -36,178 +32,10 @@ func NewConflictsTable(ctx *sql.Context, tblName doltdb.TableName, srcTable sql.
return nil, err
}
if types.IsFormat_DOLT(tbl.Format()) {
upd, ok := srcTable.(sql.UpdatableTable)
if !ok {
return nil, fmt.Errorf("%s can not have conflicts because it is not updateable", tblName)
}
return newProllyConflictsTable(ctx, tbl, upd, tblName, root, rs)
types.AssertFormat_DOLT(tbl.Format())
upd, ok := srcTable.(sql.UpdatableTable)
if !ok {
return nil, fmt.Errorf("%s can not have conflicts because it is not updateable", tblName)
}
return newNomsConflictsTable(ctx, tbl, tblName, root, rs)
}
func newNomsConflictsTable(ctx *sql.Context, tbl *doltdb.Table, tblName doltdb.TableName, root doltdb.RootValue, rs RootSetter) (sql.Table, error) {
rd, err := merge.NewConflictReader(ctx, tbl, tblName)
if err != nil {
return nil, err
}
confSch := rd.GetSchema()
sqlSch, err := sqlutil.FromDoltSchema("", doltdb.DoltConfTablePrefix+tblName.Name, confSch)
if err != nil {
return nil, err
}
return ConflictsTable{
tblName: tblName,
sqlSch: sqlSch,
root: root,
tbl: tbl,
rd: rd,
rs: rs,
}, nil
}
var _ sql.Table = ConflictsTable{}
var _ sql.DeletableTable = ConflictsTable{}
// ConflictsTable is a sql.Table implementation that provides access to the conflicts that exist for a user table
type ConflictsTable struct {
root doltdb.RootValue
rs RootSetter
tbl *doltdb.Table
rd *merge.ConflictReader
tblName doltdb.TableName
sqlSch sql.PrimaryKeySchema
}
type RootSetter interface {
SetRoot(ctx *sql.Context, root doltdb.RootValue) error
}
// Name returns the name of the table
func (ct ConflictsTable) Name() string {
return doltdb.DoltConfTablePrefix + ct.tblName.Name
}
// String returns a string identifying the table
func (ct ConflictsTable) String() string {
return doltdb.DoltConfTablePrefix + ct.tblName.Name
}
// Schema returns the sql.Schema of the table
func (ct ConflictsTable) Schema() sql.Schema {
return ct.sqlSch.Schema
}
// Collation implements the sql.Table interface.
func (ct ConflictsTable) Collation() sql.CollationID {
return sql.Collation_Default
}
// Partitions returns a PartitionIter which can be used to get all the data partitions
func (ct ConflictsTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
return index.SinglePartitionIterFromNomsMap(nil), nil
}
// PartitionRows returns a RowIter for the given partition
func (ct ConflictsTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) {
// conflict reader must be reset each time partitionRows is called.
rd, err := merge.NewConflictReader(ctx, ct.tbl, ct.tblName)
if err != nil {
return nil, err
}
return conflictRowIter{rd}, nil
}
// Deleter returns a RowDeleter for this table. The RowDeleter will get one call to Delete for each row to be deleted,
// and will end with a call to Close() to finalize the delete operation.
func (ct ConflictsTable) Deleter(ctx *sql.Context) sql.RowDeleter {
return &conflictDeleter{ct: ct, rs: ct.rs}
}
type conflictRowIter struct {
rd *merge.ConflictReader
}
// Next retrieves the next row. It will return io.EOF if it's the last row.
// After retrieving the last row, Close will be automatically closed.
func (itr conflictRowIter) Next(ctx *sql.Context) (sql.Row, error) {
cnf, err := itr.rd.NextConflict(ctx)
if err != nil {
return nil, err
}
return sqlutil.DoltRowToSqlRow(cnf, itr.rd.GetSchema())
}
// Close the iterator.
func (itr conflictRowIter) Close(*sql.Context) error {
return itr.rd.Close()
}
type conflictDeleter struct {
ct ConflictsTable
rs RootSetter
pks []types.Value
}
var _ sql.RowDeleter = &conflictDeleter{}
// Delete deletes the given row. Returns ErrDeleteRowNotFound if the row was not found. Delete will be called once for
// each row to process for the delete operation, which may involve many rows. After all rows have been processed,
// Close is called.
func (cd *conflictDeleter) Delete(ctx *sql.Context, r sql.Row) error {
cnfSch := cd.ct.rd.GetSchema()
// We could use a test VRW, but as any values which use VRWs will already exist, we can potentially save on memory usage
cnfRow, err := sqlutil.SqlRowToDoltRow(ctx, cd.ct.tbl.ValueReadWriter(), r, cnfSch)
if err != nil {
return err
}
pkVal, err := cd.ct.rd.GetKeyForConflict(ctx, cnfRow)
if err != nil {
return err
}
cd.pks = append(cd.pks, pkVal)
return nil
}
// StatementBegin implements the interface sql.TableEditor. Currently a no-op.
func (cd *conflictDeleter) StatementBegin(ctx *sql.Context) {}
// DiscardChanges implements the interface sql.TableEditor. Currently a no-op.
func (cd *conflictDeleter) DiscardChanges(ctx *sql.Context, errorEncountered error) error {
return nil
}
// StatementComplete implements the interface sql.TableEditor. Currently a no-op.
func (cd *conflictDeleter) StatementComplete(ctx *sql.Context) error {
return nil
}
// Close finalizes the delete operation, persisting the result.
func (cd *conflictDeleter) Close(ctx *sql.Context) error {
_, _, updatedTbl, err := cd.ct.tbl.ResolveConflicts(ctx, cd.pks)
if err != nil {
if errors.Is(err, doltdb.ErrNoConflictsResolved) {
return nil
}
return err
}
updatedRoot, err := cd.ct.root.PutTable(ctx, cd.ct.tblName, updatedTbl)
if err != nil {
return err
}
return cd.rs.SetRoot(ctx, updatedRoot)
return newProllyConflictsTable(ctx, tbl, upd, tblName, root, rs)
}
@@ -17,12 +17,7 @@ package dtables
import (
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
"github.com/dolthub/dolt/go/store/types"
)
@@ -32,170 +27,5 @@ func NewConstraintViolationsTable(ctx *sql.Context, tblName doltdb.TableName, ro
return newProllyCVTable(ctx, tblName, root, rs)
}
return newNomsCVTable(ctx, tblName, root, rs)
}
func newNomsCVTable(ctx *sql.Context, tblName doltdb.TableName, root doltdb.RootValue, rs RootSetter) (sql.Table, error) {
var tbl *doltdb.Table
var err error
tbl, tblName, err = getTableInsensitiveOrError(ctx, root, tblName)
if err != nil {
return nil, err
}
cvSch, err := getConstraintViolationsSchema(ctx, tbl, tblName, root)
if err != nil {
return nil, err
}
sqlSch, err := sqlutil.FromDoltSchema("", doltdb.DoltConstViolTablePrefix+tblName.Name, cvSch)
if err != nil {
return nil, err
}
return &constraintViolationsTable{
tblName: tblName,
root: root,
cvSch: cvSch,
sqlSch: sqlSch,
tbl: tbl,
rs: rs,
}, nil
}
// constraintViolationsTable is a sql.Table implementation that provides access to the constraint violations that exist
// for a user table for the old format.
type constraintViolationsTable struct {
rs RootSetter
root doltdb.RootValue
cvSch schema.Schema
tbl *doltdb.Table
tblName doltdb.TableName
sqlSch sql.PrimaryKeySchema
}
var _ sql.Table = (*constraintViolationsTable)(nil)
var _ sql.DeletableTable = (*constraintViolationsTable)(nil)
// Name implements the interface sql.Table.
func (cvt *constraintViolationsTable) Name() string {
return doltdb.DoltConstViolTablePrefix + cvt.tblName.Name
}
// String implements the interface sql.Table.
func (cvt *constraintViolationsTable) String() string {
return doltdb.DoltConstViolTablePrefix + cvt.tblName.Name
}
// Schema implements the interface sql.Table.
func (cvt *constraintViolationsTable) Schema() sql.Schema {
return cvt.sqlSch.Schema
}
// Collation implements the interface sql.Table.
func (cvt *constraintViolationsTable) Collation() sql.CollationID {
return sql.Collation_Default
}
// Partitions implements the interface sql.Table.
func (cvt *constraintViolationsTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
return index.SinglePartitionIterFromNomsMap(nil), nil
}
// PartitionRows implements the interface sql.Table.
func (cvt *constraintViolationsTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) {
cvMap, err := cvt.tbl.GetConstraintViolations(ctx)
if err != nil {
return nil, err
}
iter, err := cvMap.Iterator(ctx)
if err != nil {
return nil, err
}
return &constraintViolationsIter{cvt.cvSch, iter}, nil
}
// Deleter implements the interface sql.DeletableTable.
func (cvt *constraintViolationsTable) Deleter(ctx *sql.Context) sql.RowDeleter {
cvMap, err := cvt.tbl.GetConstraintViolations(ctx)
if err != nil {
panic(err)
}
return &constraintViolationsDeleter{cvt, cvMap.Edit()}
}
// constraintViolationsIter is the iterator for constraintViolationsTable.
type constraintViolationsIter struct {
dSch schema.Schema
iter types.MapIterator
}
var _ sql.RowIter = (*constraintViolationsIter)(nil)
// Next implements the interface sql.RowIter.
func (cvi *constraintViolationsIter) Next(ctx *sql.Context) (sql.Row, error) {
k, v, err := cvi.iter.NextTuple(ctx)
if err != nil {
return nil, err
}
dRow, err := row.FromNoms(cvi.dSch, k, v)
if err != nil {
return nil, err
}
return sqlutil.DoltRowToSqlRow(dRow, cvi.dSch)
}
// Close implements the interface sql.RowIter.
func (cvi *constraintViolationsIter) Close(*sql.Context) error {
return nil
}
// constraintViolationsDeleter handles deletions on the generated constraintViolationsTable.
type constraintViolationsDeleter struct {
cvt *constraintViolationsTable
editor *types.MapEditor
}
var _ sql.RowDeleter = (*constraintViolationsDeleter)(nil)
// Delete implements the interface sql.RowDeleter.
func (cvd *constraintViolationsDeleter) Delete(ctx *sql.Context, r sql.Row) error {
dRow, err := sqlutil.SqlRowToDoltRow(ctx, cvd.cvt.tbl.ValueReadWriter(), r, cvd.cvt.cvSch)
if err != nil {
return err
}
key, err := dRow.NomsMapKey(cvd.cvt.cvSch).Value(ctx)
if err != nil {
return err
}
cvd.editor.Remove(key)
return nil
}
// StatementBegin implements the interface sql.TableEditor. Currently a no-op.
func (cvd *constraintViolationsDeleter) StatementBegin(ctx *sql.Context) {}
// DiscardChanges implements the interface sql.TableEditor. Currently a no-op.
func (cvd *constraintViolationsDeleter) DiscardChanges(ctx *sql.Context, errorEncountered error) error {
return nil
}
// StatementComplete implements the interface sql.TableEditor. Currently a no-op.
func (cvd *constraintViolationsDeleter) StatementComplete(ctx *sql.Context) error {
return nil
}
// Close implements the interface sql.RowDeleter.
func (cvd *constraintViolationsDeleter) Close(ctx *sql.Context) error {
updatedMap, err := cvd.editor.Map(ctx)
if err != nil {
return err
}
updatedTbl, err := cvd.cvt.tbl.SetConstraintViolations(ctx, updatedMap)
if err != nil {
return err
}
updatedRoot, err := cvd.cvt.root.PutTable(ctx, cvd.cvt.tblName, updatedTbl)
if err != nil {
return err
}
return cvd.cvt.rs.SetRoot(ctx, updatedRoot)
panic("Unsupported format: " + root.VRW().Format().VersionString())
}
+4 -164
View File
@@ -21,32 +21,15 @@ import (
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/libraries/doltcore/diff"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/rowconv"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
"github.com/dolthub/dolt/go/store/prolly"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/types"
"github.com/dolthub/dolt/go/store/val"
)
// ldDiffRowItr is a sql.RowIter implementation which iterates over an LD formated DB in order to generate the
// dolt_diff_{table} results. This is legacy code at this point, as the DOLT format is what we'll support going forward.
type ldDiffRowItr struct {
ad diff.RowDiffer
diffSrc *diff.RowDiffSource
joiner *rowconv.Joiner
sch schema.Schema
fromCommitInfo commitInfo
toCommitInfo commitInfo
}
var _ sql.RowIter = &ldDiffRowItr{}
type commitInfo struct {
date *types.Timestamp
name types.String
@@ -54,147 +37,6 @@ type commitInfo struct {
dateTag uint64
}
func newLdDiffIter(ctx *sql.Context, ddb *doltdb.DoltDB, joiner *rowconv.Joiner, dp DiffPartition, lookup sql.IndexLookup) (*ldDiffRowItr, error) {
fromData, fromSch, err := tableData(ctx, dp.from, ddb)
if err != nil {
return nil, err
}
toData, toSch, err := tableData(ctx, dp.to, ddb)
if err != nil {
return nil, err
}
fromConv, err := dp.rowConvForSchema(ctx, ddb.ValueReadWriter(), dp.fromSch, fromSch)
if err != nil {
return nil, err
}
toConv, err := dp.rowConvForSchema(ctx, ddb.ValueReadWriter(), dp.toSch, toSch)
if err != nil {
return nil, err
}
sch := joiner.GetSchema()
toCol, _ := sch.GetAllCols().GetByName(toCommit)
fromCol, _ := sch.GetAllCols().GetByName(fromCommit)
toDateCol, _ := sch.GetAllCols().GetByName(toCommitDate)
fromDateCol, _ := sch.GetAllCols().GetByName(fromCommitDate)
fromCmInfo := commitInfo{name: types.String(dp.fromName), date: dp.fromDate, nameTag: fromCol.Tag, dateTag: fromDateCol.Tag}
toCmInfo := commitInfo{name: types.String(dp.toName), date: dp.toDate, nameTag: toCol.Tag, dateTag: toDateCol.Tag}
rd := diff.NewRowDiffer(ctx, ddb.Format(), fromSch, toSch, 1024)
// TODO (dhruv) don't cast to noms map
// Use index lookup if it exists
if lookup.IsEmpty() {
rd.Start(ctx, durable.NomsMapFromIndex(fromData), durable.NomsMapFromIndex(toData))
} else {
ranges, err := index.NomsRangesFromIndexLookup(ctx, lookup) // TODO: this is a testing method
if err != nil {
return nil, err
}
// TODO: maybe just use Check
rangeFunc := func(ctx context.Context, val types.Value) (bool, bool, error) {
v, ok := val.(types.Tuple)
if !ok {
return false, false, nil
}
return ranges[0].Check.Check(ctx, ddb.ValueReadWriter(), v)
}
rd.StartWithRange(ctx, durable.NomsMapFromIndex(fromData), durable.NomsMapFromIndex(toData), ranges[0].Start, rangeFunc)
}
src := diff.NewRowDiffSource(rd, joiner, ctx.Warn)
src.AddInputRowConversion(fromConv, toConv)
return &ldDiffRowItr{
ad: rd,
diffSrc: src,
joiner: joiner,
sch: joiner.GetSchema(),
fromCommitInfo: fromCmInfo,
toCommitInfo: toCmInfo,
}, nil
}
// Next returns the next row
func (itr *ldDiffRowItr) Next(ctx *sql.Context) (sql.Row, error) {
r, err := itr.diffSrc.NextDiff()
if err != nil {
return nil, err
}
toAndFromRows, err := itr.joiner.Split(r)
if err != nil {
return nil, err
}
_, hasTo := toAndFromRows[diff.To]
_, hasFrom := toAndFromRows[diff.From]
r, err = r.SetColVal(itr.toCommitInfo.nameTag, types.String(itr.toCommitInfo.name), itr.sch)
if err != nil {
return nil, err
}
r, err = r.SetColVal(itr.fromCommitInfo.nameTag, types.String(itr.fromCommitInfo.name), itr.sch)
if err != nil {
return nil, err
}
if itr.toCommitInfo.date != nil {
r, err = r.SetColVal(itr.toCommitInfo.dateTag, *itr.toCommitInfo.date, itr.sch)
if err != nil {
return nil, err
}
}
if itr.fromCommitInfo.date != nil {
r, err = r.SetColVal(itr.fromCommitInfo.dateTag, *itr.fromCommitInfo.date, itr.sch)
if err != nil {
return nil, err
}
}
sqlRow, err := sqlutil.DoltRowToSqlRow(r, itr.sch)
if err != nil {
return nil, err
}
if hasTo && hasFrom {
sqlRow = append(sqlRow, diffTypeModified)
} else if hasTo && !hasFrom {
sqlRow = append(sqlRow, diffTypeAdded)
} else {
sqlRow = append(sqlRow, diffTypeRemoved)
}
return sqlRow, nil
}
// Close closes the iterator
func (itr *ldDiffRowItr) Close(*sql.Context) (err error) {
defer itr.ad.Close()
defer func() {
closeErr := itr.diffSrc.Close()
if err == nil {
err = closeErr
}
}()
return nil
}
type commitInfo2 struct {
ts *time.Time
name string
@@ -543,24 +385,22 @@ func maybeTime(t *time.Time) interface{} {
return nil
}
//------------------------------------
// ------------------------------------
// diffPartitionRowIter
//------------------------------------
// ------------------------------------
var _ sql.RowIter = (*diffPartitionRowIter)(nil)
type diffPartitionRowIter struct {
ddb *doltdb.DoltDB
joiner *rowconv.Joiner
currentPartition *DiffPartition
currentRowIter *sql.RowIter
}
func NewDiffPartitionRowIter(partition *DiffPartition, ddb *doltdb.DoltDB, joiner *rowconv.Joiner) *diffPartitionRowIter {
func NewDiffPartitionRowIter(partition *DiffPartition, ddb *doltdb.DoltDB) *diffPartitionRowIter {
return &diffPartitionRowIter{
currentPartition: partition,
ddb: ddb,
joiner: joiner,
}
}
@@ -570,7 +410,7 @@ func (itr *diffPartitionRowIter) Next(ctx *sql.Context) (sql.Row, error) {
return nil, io.EOF
}
if itr.currentRowIter == nil {
rowIter, err := itr.currentPartition.GetRowIter(ctx, itr.ddb, itr.joiner)
rowIter, err := itr.currentPartition.GetRowIter(ctx)
if err != nil {
return nil, err
}
@@ -67,7 +67,6 @@ type DiffTable struct {
head *doltdb.Commit
headCommitClosure *prolly.CommitClosure
table *doltdb.Table
joiner *rowconv.Joiner
tableName doltdb.TableName
sqlSch sql.PrimaryKeySchema
partitionFilters []sql.Expression
@@ -105,7 +104,7 @@ func NewDiffTable(ctx *sql.Context, dbName string, tblName doltdb.TableName, ddb
return nil, err
}
diffTableSchema, j, err := GetDiffTableSchemaAndJoiner(ddb.Format(), sch, sch)
diffTableSchema, err := GetDiffTableSchemaAndJoiner(ddb.Format(), sch, sch)
if err != nil {
return nil, err
}
@@ -125,7 +124,6 @@ func NewDiffTable(ctx *sql.Context, dbName string, tblName doltdb.TableName, ddb
sqlSch: sqlSch,
partitionFilters: nil,
table: table,
joiner: j,
}, nil
}
@@ -250,7 +248,7 @@ func (dt *DiffTable) HeadHash() (hash.Hash, error) {
func (dt *DiffTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) {
dp := part.(DiffPartition)
return dp.GetRowIter(ctx, dt.ddb, dt.joiner)
return dp.GetRowIter(ctx)
}
func (dt *DiffTable) LookupPartitions(ctx *sql.Context, lookup sql.IndexLookup) (sql.PartitionIter, error) {
@@ -677,7 +675,7 @@ func (dp DiffPartition) Key() []byte {
return []byte(dp.toName + dp.fromName)
}
func (dp DiffPartition) GetRowIter(ctx *sql.Context, ddb *doltdb.DoltDB, joiner *rowconv.Joiner) (sql.RowIter, error) {
func (dp DiffPartition) GetRowIter(ctx *sql.Context) (sql.RowIter, error) {
return newProllyDiffIter(ctx, dp, dp.fromSch, dp.toSch, dp.ranges)
}
@@ -709,7 +707,7 @@ func (dp *DiffPartition) isDiffablePartition(ctx *sql.Context) (simpleDiff bool,
return false, false, err
}
easyDiff := schema.ArePrimaryKeySetsDiffable(dp.from.Format(), fromSch, toSch)
easyDiff := schema.ArePrimaryKeySetsDiffable(fromSch, toSch)
if easyDiff {
return true, false, nil
}
@@ -910,37 +908,12 @@ func (dp DiffPartition) rowConvForSchema(ctx context.Context, vrw types.ValueRea
// GetDiffTableSchemaAndJoiner returns the schema for the diff table given a
// target schema for a row |sch|. In the old storage format, it also returns the
// associated joiner.
func GetDiffTableSchemaAndJoiner(format *types.NomsBinFormat, fromSch, toSch schema.Schema) (diffTableSchema schema.Schema, j *rowconv.Joiner, err error) {
func GetDiffTableSchemaAndJoiner(format *types.NomsBinFormat, fromSch, toSch schema.Schema) (diffTableSchema schema.Schema, err error) {
if format == types.Format_DOLT {
diffTableSchema, err = CalculateDiffSchema(fromSch, toSch)
if err != nil {
return nil, nil, err
}
return CalculateDiffSchema(fromSch, toSch)
} else {
fromSch, toSch, err = expandFromToSchemas(fromSch, toSch)
if err != nil {
return nil, nil, err
}
j, err = rowconv.NewJoiner(
[]rowconv.NamedSchema{{Name: diff.To, Sch: toSch}, {Name: diff.From, Sch: fromSch}},
map[string]rowconv.ColNamingFunc{
diff.To: diff.ToColNamer,
diff.From: diff.FromColNamer,
})
if err != nil {
return nil, nil, err
}
diffTableSchema = j.GetSchema()
fullDiffCols := diffTableSchema.GetAllCols()
fullDiffCols = fullDiffCols.Append(
schema.NewColumn(diffTypeColName, schema.DiffTypeTag, types.StringKind, false),
)
diffTableSchema = schema.MustSchemaFromCols(fullDiffCols)
panic("Unsupported format for diff table schema calculation: " + format.VersionString())
}
return
}
// expandFromToSchemas converts input schemas to schemas appropriate for diffs. One argument must be
@@ -25,3 +25,8 @@ type VersionableTable interface {
sql.Table
LockedToRoot(ctx *sql.Context, root doltdb.RootValue) (sql.IndexAddressableTable, error)
}
// RootSetter is an interface that can be used to set the root of a working set.
type RootSetter interface {
SetRoot(ctx *sql.Context, root doltdb.RootValue) error
}
@@ -490,25 +490,16 @@ func TestInsertIgnoreInto(t *testing.T) {
// TODO: merge this into the above test when we remove old format
func TestInsertDuplicateKeyKeyless(t *testing.T) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip()
}
enginetest.TestInsertDuplicateKeyKeyless(t, newDoltHarness(t))
}
// TODO: merge this into the above test when we remove old format
func TestInsertDuplicateKeyKeylessPrepared(t *testing.T) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip()
}
enginetest.TestInsertDuplicateKeyKeylessPrepared(t, newDoltHarness(t))
}
// TODO: merge this into the above test when we remove old format
func TestIgnoreIntoWithDuplicateUniqueKeyKeyless(t *testing.T) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip()
}
h := newDoltHarness(t)
defer h.Close()
enginetest.TestIgnoreIntoWithDuplicateUniqueKeyKeyless(t, h)
@@ -516,9 +507,6 @@ func TestIgnoreIntoWithDuplicateUniqueKeyKeyless(t *testing.T) {
// TODO: merge this into the above test when we remove old format
func TestIgnoreIntoWithDuplicateUniqueKeyKeylessPrepared(t *testing.T) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip()
}
enginetest.TestIgnoreIntoWithDuplicateUniqueKeyKeylessPrepared(t, newDoltHarness(t))
}
@@ -601,17 +589,14 @@ func TestSpatialScriptsPrepared(t *testing.T) {
}
func TestSpatialIndexScripts(t *testing.T) {
skipOldFormat(t)
enginetest.TestSpatialIndexScripts(t, newDoltHarness(t))
}
func TestSpatialIndexScriptsPrepared(t *testing.T) {
skipOldFormat(t)
enginetest.TestSpatialIndexScriptsPrepared(t, newDoltHarness(t))
}
func TestSpatialIndexPlans(t *testing.T) {
skipOldFormat(t)
enginetest.TestSpatialIndexPlans(t, newDoltHarness(t))
}
@@ -622,18 +607,12 @@ func TestTruncate(t *testing.T) {
}
func TestConvert(t *testing.T) {
if types.IsFormat_LD(types.Format_Default) {
t.Skip("noms format has outdated type enforcement")
}
h := newDoltHarness(t)
defer h.Close()
enginetest.TestConvertPrepared(t, h)
}
func TestConvertPrepared(t *testing.T) {
if types.IsFormat_LD(types.Format_Default) {
t.Skip("noms format has outdated type enforcement")
}
h := newDoltHarness(t)
defer h.Close()
enginetest.TestConvertPrepared(t, h)
@@ -868,7 +847,6 @@ func TestCreateDatabase(t *testing.T) {
}
func TestBlobs(t *testing.T) {
skipOldFormat(t)
h := newDoltHarness(t)
defer h.Close()
enginetest.TestBlobs(t, h)
@@ -899,14 +877,11 @@ func TestVectorType(t *testing.T) {
}
func TestIndexPrefix(t *testing.T) {
skipOldFormat(t)
harness := newDoltHarness(t)
RunIndexPrefixTest(t, harness)
}
func TestBigBlobs(t *testing.T) {
skipOldFormat(t)
h := newDoltHarness(t)
RunBigBlobsTest(t, h)
}
@@ -914,7 +889,6 @@ func TestBigBlobs(t *testing.T) {
func TestAdaptiveEncoding(t *testing.T) {
defer func() { schema.UseAdaptiveEncoding = false }()
schema.UseAdaptiveEncoding = true
skipOldFormat(t)
RunTestAdaptiveEncoding(t, newDoltHarness(t), AdaptiveEncodingTestType_Blob, AdaptiveEncodingTestPurpose_Representation)
RunTestAdaptiveEncoding(t, newDoltHarness(t), AdaptiveEncodingTestType_Blob, AdaptiveEncodingTestPurpose_Correctness)
@@ -956,9 +930,6 @@ func TestForeignKeyBranchesPrepared(t *testing.T) {
}
func TestFulltextIndexes(t *testing.T) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip("FULLTEXT is not supported on the old format")
}
if runtime.GOOS == "windows" && os.Getenv("CI") != "" {
t.Skip("For some reason, this is flaky only on Windows CI.")
}
@@ -1341,21 +1312,6 @@ func TestDoltPreviewMergeConflictsPrepared(t *testing.T) {
RunDoltPreviewMergeConflictsPreparedTests(t, h)
}
// these tests are temporary while there is a difference between the old format
// and new format merge behaviors.
func TestOldFormatMergeConflictsAndCVs(t *testing.T) {
if types.IsFormat_DOLT(types.Format_Default) {
t.Skip()
}
for _, script := range OldFormatMergeConflictsAndCVsScripts {
func() {
h := newDoltHarness(t)
defer h.Close()
enginetest.TestScript(t, h, script)
}()
}
}
func TestDoltReset(t *testing.T) {
h := newDoltEnginetestHarness(t)
RunDoltResetTest(t, h)
@@ -1939,21 +1895,18 @@ func TestPreparedStatements(t *testing.T) {
}
func TestCharsetCollationEngine(t *testing.T) {
skipOldFormat(t)
h := newDoltHarness(t)
defer h.Close()
enginetest.TestCharsetCollationEngine(t, h)
}
func TestCharsetCollationWire(t *testing.T) {
skipOldFormat(t)
harness := newDoltHarness(t)
defer harness.Close()
enginetest.TestCharsetCollationWire(t, harness, newSessionBuilder(harness))
}
func TestDatabaseCollationWire(t *testing.T) {
skipOldFormat(t)
harness := newDoltHarness(t)
defer harness.Close()
enginetest.TestDatabaseCollationWire(t, harness, newSessionBuilder(harness))
@@ -43,7 +43,6 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables"
"github.com/dolthub/dolt/go/store/datas"
"github.com/dolthub/dolt/go/store/types"
)
const skipPreparedFlag = "DOLT_SKIP_PREPARED_ENGINETESTS"
@@ -802,9 +801,7 @@ func RunDoltDdlScripts(t *testing.T, harness DoltEnginetestHarness) {
require.NoError(t, err)
enginetest.TestScriptWithEngine(t, e, harness, script)
}
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip("not fixing unique index on keyless tables for old format")
}
for _, script := range AddIndexScripts {
e, err := harness.NewEngine(t)
require.NoError(t, err)
@@ -952,21 +949,16 @@ func RunDoltConflictsTableNameTableTests(t *testing.T, h DoltEnginetestHarness)
}()
}
if types.IsFormat_DOLT(types.Format_Default) {
for _, script := range Dolt1ConflictTableNameTableTests {
func() {
h := h.NewHarness(t)
defer h.Close()
enginetest.TestScript(t, h, script)
}()
}
for _, script := range Dolt1ConflictTableNameTableTests {
func() {
h := h.NewHarness(t)
defer h.Close()
enginetest.TestScript(t, h, script)
}()
}
}
func RunKeylessDoltMergeCVsAndConflictsTests(t *testing.T, h DoltEnginetestHarness) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip()
}
for _, script := range KeylessMergeCVsAndConflictsScripts {
func() {
h := h.NewHarness(t)
@@ -1174,9 +1166,6 @@ func RunUnscopedDiffSystemTableTestsPrepared(t *testing.T, h DoltEnginetestHarne
}
func RunColumnDiffSystemTableTests(t *testing.T, h DoltEnginetestHarness) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip("correct behavior of dolt_column_diff only guaranteed on new format")
}
for _, test := range ColumnDiffSystemTableScriptTests {
t.Run(test.Name, func(t *testing.T) {
enginetest.TestScript(t, h.NewHarness(t), test)
@@ -1185,9 +1174,6 @@ func RunColumnDiffSystemTableTests(t *testing.T, h DoltEnginetestHarness) {
}
func RunColumnDiffSystemTableTestsPrepared(t *testing.T, h DoltEnginetestHarness) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip("correct behavior of dolt_column_diff only guaranteed on new format")
}
for _, test := range ColumnDiffSystemTableScriptTests {
t.Run(test.Name, func(t *testing.T) {
enginetest.TestScriptPrepared(t, h.NewHarness(t), test)
@@ -1393,10 +1379,6 @@ func RunCommitDiffSystemTableTestsPrepared(t *testing.T, harness DoltEnginetestH
}
func RunDoltDiffSystemTableTests(t *testing.T, h DoltEnginetestHarness) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip("only new format support system table indexing")
}
for _, test := range DiffSystemTableScriptTests {
t.Run(test.Name, func(t *testing.T) {
h = h.NewHarness(t)
@@ -1406,23 +1388,17 @@ func RunDoltDiffSystemTableTests(t *testing.T, h DoltEnginetestHarness) {
})
}
if types.IsFormat_DOLT(types.Format_Default) {
for _, test := range Dolt1DiffSystemTableScripts {
func() {
h = h.NewHarness(t)
defer h.Close()
h.Setup(setup.MydbData)
enginetest.TestScript(t, h, test)
}()
}
for _, test := range Dolt1DiffSystemTableScripts {
func() {
h = h.NewHarness(t)
defer h.Close()
h.Setup(setup.MydbData)
enginetest.TestScript(t, h, test)
}()
}
}
func RunDoltDiffSystemTableTestsPrepared(t *testing.T, h DoltEnginetestHarness) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip("only new format support system table indexing")
}
for _, test := range DiffSystemTableScriptTests {
t.Run(test.Name, func(t *testing.T) {
h = h.NewHarness(t)
@@ -1432,23 +1408,17 @@ func RunDoltDiffSystemTableTestsPrepared(t *testing.T, h DoltEnginetestHarness)
})
}
if types.IsFormat_DOLT(types.Format_Default) {
for _, test := range Dolt1DiffSystemTableScripts {
func() {
h = h.NewHarness(t)
defer h.Close()
h.Setup(setup.MydbData)
enginetest.TestScriptPrepared(t, h, test)
}()
}
for _, test := range Dolt1DiffSystemTableScripts {
func() {
h = h.NewHarness(t)
defer h.Close()
h.Setup(setup.MydbData)
enginetest.TestScriptPrepared(t, h, test)
}()
}
}
func RunNonlocalTableTests(t *testing.T, h DoltEnginetestHarness) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip("only new format support system table indexing")
}
for _, test := range NonlocalScripts {
t.Run(test.Name, func(t *testing.T) {
h = h.NewHarness(t)
@@ -1460,10 +1430,6 @@ func RunNonlocalTableTests(t *testing.T, h DoltEnginetestHarness) {
}
func RunNonlocalTableTestsPrepared(t *testing.T, h DoltEnginetestHarness) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip("only new format support system table indexing")
}
for _, test := range NonlocalScripts {
t.Run(test.Name, func(t *testing.T) {
h = h.NewHarness(t)
@@ -1519,10 +1485,6 @@ func RunQueryDiffTests(t *testing.T, harness DoltEnginetestHarness) {
}
func RunSystemTableIndexesTests(t *testing.T, harness DoltEnginetestHarness) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip("only new format support system table indexing")
}
for _, stt := range SystemTableIndexTests {
harness = harness.NewHarness(t).WithParallelism(1)
defer harness.Close()
@@ -1572,10 +1534,6 @@ var biasedCosters = []memo.Coster{
}
func RunSystemTableIndexesTestsPrepared(t *testing.T, harness DoltEnginetestHarness) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip("only new format support system table indexing")
}
for _, stt := range SystemTableIndexTests {
harness = harness.NewHarness(t).WithParallelism(2)
defer harness.Close()
@@ -1919,12 +1877,7 @@ func RunDoltVerifyConstraintsTests(t *testing.T, harness DoltEnginetestHarness)
}
func RunDoltStorageFormatTests(t *testing.T, h DoltEnginetestHarness) {
var expectedFormatString string
if types.IsFormat_DOLT(types.Format_Default) {
expectedFormatString = "NEW ( __DOLT__ )"
} else {
expectedFormatString = fmt.Sprintf("OLD ( %s )", types.Format_Default.VersionString())
}
var expectedFormatString = "NEW ( __DOLT__ )"
script := queries.ScriptTest{
Name: "dolt storage format function works",
Assertions: []queries.ScriptTestAssertion{
@@ -1939,7 +1892,6 @@ func RunDoltStorageFormatTests(t *testing.T, h DoltEnginetestHarness) {
}
func RunThreeWayMergeWithSchemaChangeScripts(t *testing.T, h DoltEnginetestHarness) {
skipOldFormat(t)
runMergeScriptTestsInBothDirections(t, SchemaChangeTestsBasicCases, "basic cases", false)
runMergeScriptTestsInBothDirections(t, SchemaChangeTestsForDataConflicts, "data conflicts", false)
runMergeScriptTestsInBothDirections(t, SchemaChangeTestsCollations, "collation changes", false)
@@ -1962,7 +1914,6 @@ func RunThreeWayMergeWithSchemaChangeScripts(t *testing.T, h DoltEnginetestHarne
}
func RunThreeWayMergeWithSchemaChangeScriptsPrepared(t *testing.T, h DoltEnginetestHarness) {
skipOldFormat(t)
runMergeScriptTestsInBothDirections(t, SchemaChangeTestsBasicCases, "basic cases", false)
runMergeScriptTestsInBothDirections(t, SchemaChangeTestsForDataConflicts, "data conflicts", false)
runMergeScriptTestsInBothDirections(t, SchemaChangeTestsCollations, "collation changes", false)
@@ -2032,12 +1983,6 @@ var newFormatSkippedScripts = []string{
"Multiple indexes on the same columns in a different order",
}
func skipOldFormat(t *testing.T) {
if !types.IsFormat_DOLT(types.Format_Default) {
t.Skip()
}
}
func skipPreparedTests(t *testing.T) {
if skipPrepared {
t.Skip("skip prepared")
@@ -4594,362 +4594,6 @@ var SchemaConflictScripts = []queries.ScriptTest{
},
}
// OldFormatMergeConflictsAndCVsScripts tests old format merge behavior
// where violations are appended and merges are aborted if there are existing
// violations and/or conflicts.
var OldFormatMergeConflictsAndCVsScripts = []queries.ScriptTest{
{
Name: "merging branches into a constraint violated head. Any new violations are appended",
SetUpScript: []string{
"CREATE table parent (pk int PRIMARY KEY, col1 int);",
"CREATE table child (pk int PRIMARY KEY, parent_fk int, FOREIGN KEY (parent_fk) REFERENCES parent(pk));",
"CREATE table other (pk int);",
"CALL DOLT_ADD('.')",
"INSERT INTO parent VALUES (1, 1), (2, 2);",
"CALL DOLT_COMMIT('-am', 'setup');",
"CALL DOLT_BRANCH('branch1');",
"CALL DOLT_BRANCH('branch2');",
},
Assertions: []queries.ScriptTestAssertion{
{
// we need dolt_force_transaction_commit because we want to
// transaction commit constraint violations that occur as a
// result of a merge.
Query: "set autocommit = off, dolt_force_transaction_commit = on",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
Query: "DELETE FROM parent where pk = 1;",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "CALL DOLT_COMMIT('-am', 'delete parent 1');",
Expected: []sql.Row{{doltCommit}},
},
{
Query: "CALL DOLT_CHECKOUT('branch1');",
Expected: []sql.Row{{0, "Switched to branch 'branch1'"}},
},
{
Query: "INSERT INTO CHILD VALUES (1, 1);",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "CALL DOLT_COMMIT('-am', 'insert child of parent 1');",
Expected: []sql.Row{{doltCommit}},
},
{
Query: "CALL DOLT_CHECKOUT('main');",
Expected: []sql.Row{{0, "Switched to branch 'main'"}},
},
{
Query: "CALL DOLT_MERGE('branch1');",
Expected: []sql.Row{{"", 0, 1, "conflicts found"}},
},
{
Query: "SELECT violation_type, pk, parent_fk from dolt_constraint_violations_child;",
Expected: []sql.Row{{uint16(1), 1, 1}},
},
{
Query: "COMMIT;",
Expected: []sql.Row{},
},
{
Query: "CALL DOLT_COMMIT('-am', 'commit constraint violations');",
ExpectedErrStr: "error: the table(s) child have constraint violations",
},
{
Query: "CALL DOLT_COMMIT('-afm', 'commit constraint violations');",
Expected: []sql.Row{{doltCommit}},
},
{
Query: "CALL DOLT_BRANCH('branch3');",
Expected: []sql.Row{{0}},
},
{
Query: "DELETE FROM parent where pk = 2;",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "CALL DOLT_COMMIT('-afm', 'remove parent 2');",
Expected: []sql.Row{{doltCommit}},
},
{
Query: "CALL DOLT_CHECKOUT('branch2');",
Expected: []sql.Row{{0, "Switched to branch 'branch2'"}},
},
{
Query: "INSERT INTO OTHER VALUES (1);",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "CALL DOLT_COMMIT('-am', 'non-fk insert');",
Expected: []sql.Row{{doltCommit}},
},
{
Query: "CALL DOLT_CHECKOUT('main');",
Expected: []sql.Row{{0, "Switched to branch 'main'"}},
},
{
Query: "CALL DOLT_MERGE('branch2');",
Expected: []sql.Row{{"", 0, 1, "conflicts found"}},
},
{
Query: "SELECT violation_type, pk, parent_fk from dolt_constraint_violations_child;",
Expected: []sql.Row{{uint16(1), 1, 1}},
},
{
Query: "COMMIT;",
Expected: []sql.Row{},
},
{
Query: "CALL DOLT_COMMIT('-am', 'commit non-conflicting merge');",
ExpectedErrStr: "error: the table(s) child have constraint violations",
},
{
Query: "CALL DOLT_COMMIT('-afm', 'commit non-conflicting merge');",
Expected: []sql.Row{{doltCommit}},
},
{
Query: "CALL DOLT_CHECKOUT('branch3');",
Expected: []sql.Row{{0, "Switched to branch 'branch3'"}},
},
{
Query: "INSERT INTO CHILD VALUES (2, 2);",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "CALL DOLT_COMMIT('-afm', 'add child of parent 2');",
Expected: []sql.Row{{doltCommit}},
},
{
Query: "CALL DOLT_CHECKOUT('main');",
Expected: []sql.Row{{0, "Switched to branch 'main'"}},
},
{
Query: "CALL DOLT_MERGE('branch3');",
Expected: []sql.Row{{"", 0, 1, "conflicts found"}},
},
{
Query: "SELECT violation_type, pk, parent_fk from dolt_constraint_violations_child;",
Expected: []sql.Row{{uint16(1), 1, 1}, {uint16(1), 2, 2}},
},
},
},
{
Name: "conflicting merge aborts when conflicts and violations already exist",
SetUpScript: []string{
"CREATE table parent (pk int PRIMARY KEY, col1 int);",
"CREATE table child (pk int PRIMARY KEY, parent_fk int, FOREIGN KEY (parent_fk) REFERENCES parent(pk));",
"CALL DOLT_ADD('.')",
"INSERT INTO parent VALUES (1, 1), (2, 1);",
"CALL DOLT_COMMIT('-am', 'create table with data');",
"CALL DOLT_BRANCH('other');",
"CALL DOLT_BRANCH('other2');",
"UPDATE parent SET col1 = 2 where pk = 1;",
"DELETE FROM parent where pk = 2;",
"CALL DOLT_COMMIT('-am', 'updating col1 to 2 and remove pk = 2');",
"CALL DOLT_CHECKOUT('other');",
"UPDATE parent SET col1 = 3 where pk = 1;",
"INSERT into child VALUEs (1, 2);",
"CALL DOLT_COMMIT('-am', 'updating col1 to 3 and adding child of pk 2');",
"CALL DOLT_CHECKOUT('other2')",
"UPDATE parent SET col1 = 4 where pk = 1",
"CALL DOLT_COMMIT('-am', 'updating col1 to 4');",
"CALL DOLT_CHECKOUT('main');",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "SET dolt_force_transaction_commit = 1",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
Query: "CALL DOLT_MERGE('other');",
Expected: []sql.Row{{"", 0, 1, "conflicts found"}},
},
{
Query: "SELECT * from parent;",
Expected: []sql.Row{{1, 2}},
},
{
Query: "SELECT * from child;",
Expected: []sql.Row{{1, 2}},
},
{
Query: "SELECT base_col1, base_pk, our_col1, our_pk, their_col1, their_pk from dolt_conflicts_parent;",
Expected: []sql.Row{{1, 1, 2, 1, 3, 1}},
},
{
Query: "SELECT violation_type, pk, parent_fk from dolt_constraint_violations_child;",
Expected: []sql.Row{{uint16(1), 1, 2}},
},
// commit so we can merge again
{
Query: "CALL DOLT_COMMIT('-afm', 'committing merge conflicts');",
Expected: []sql.Row{{doltCommit}},
},
{
Query: "CALL DOLT_MERGE('other2');",
ExpectedErrStr: "existing unresolved conflicts would be overridden by new conflicts produced by merge. Please resolve them and try again",
},
{
Query: "SELECT * from parent;",
Expected: []sql.Row{{1, 2}},
},
{
Query: "SELECT * from child;",
Expected: []sql.Row{{1, 2}},
},
{
Query: "SELECT base_col1, base_pk, our_col1, our_pk, their_col1, their_pk from dolt_conflicts_parent;",
Expected: []sql.Row{{1, 1, 2, 1, 3, 1}},
},
{
Query: "SELECT violation_type, pk, parent_fk from dolt_constraint_violations_child;",
Expected: []sql.Row{{uint16(1), 1, 2}},
},
},
},
{
Name: "non-conflicting / non-violating merge succeeds when conflicts and violations already exist",
SetUpScript: []string{
"CREATE table parent (pk int PRIMARY KEY, col1 int);",
"CREATE table child (pk int PRIMARY KEY, parent_fk int, FOREIGN KEY (parent_fk) REFERENCES parent(pk));",
"CALL DOLT_ADD('.')",
"INSERT INTO parent VALUES (1, 1), (2, 1);",
"CALL DOLT_COMMIT('-am', 'create table with data');",
"CALL DOLT_BRANCH('other');",
"CALL DOLT_BRANCH('other2');",
"UPDATE parent SET col1 = 2 where pk = 1;",
"DELETE FROM parent where pk = 2;",
"CALL DOLT_COMMIT('-am', 'updating col1 to 2 and remove pk = 2');",
"CALL DOLT_CHECKOUT('other');",
"UPDATE parent SET col1 = 3 where pk = 1;",
"INSERT into child VALUES (1, 2);",
"CALL DOLT_COMMIT('-am', 'updating col1 to 3 and adding child of pk 2');",
"CALL DOLT_CHECKOUT('other2')",
"INSERT INTO parent values (3, 1);",
"CALL DOLT_COMMIT('-am', 'insert parent with pk 3');",
"CALL DOLT_CHECKOUT('main');",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "SET dolt_force_transaction_commit = 1;",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
Query: "CALL DOLT_MERGE('other');",
Expected: []sql.Row{{"", 0, 1, "conflicts found"}},
},
{
Query: "SELECT * from parent;",
Expected: []sql.Row{{1, 2}},
},
{
Query: "SELECT * from child;",
Expected: []sql.Row{{1, 2}},
},
{
Query: "SELECT base_col1, base_pk, our_col1, our_pk, their_col1, their_pk from dolt_conflicts_parent;",
Expected: []sql.Row{{1, 1, 2, 1, 3, 1}},
},
{
Query: "SELECT violation_type, pk, parent_fk from dolt_constraint_violations_child;",
Expected: []sql.Row{{uint16(1), 1, 2}},
},
// commit so we can merge again
{
Query: "CALL DOLT_COMMIT('-afm', 'committing merge conflicts');",
Expected: []sql.Row{{doltCommit}},
},
{
Query: "CALL DOLT_MERGE('other2');",
Expected: []sql.Row{{"", 0, 1, "conflicts found"}},
},
{
Query: "SELECT * from parent;",
Expected: []sql.Row{{1, 2}, {3, 1}},
},
{
Query: "SELECT * from child;",
Expected: []sql.Row{{1, 2}},
},
{
Query: "SELECT base_col1, base_pk, our_col1, our_pk, their_col1, their_pk from dolt_conflicts_parent;",
Expected: []sql.Row{{1, 1, 2, 1, 3, 1}},
},
{
Query: "SELECT violation_type, pk, parent_fk from dolt_constraint_violations_child;",
Expected: []sql.Row{{uint16(1), 1, 2}},
},
},
},
// Unique key violations
{
Name: "unique key violations that already exist in the left abort the merge with an error",
SetUpScript: []string{
"SET dolt_force_transaction_commit = on;",
"CREATE TABLE t (pk int PRIMARY KEY, col1 int);",
"CALL DOLT_ADD('.')",
"CALL DOLT_COMMIT('-am', 'table');",
"CALL DOLT_BRANCH('right');",
"INSERT INTO t VALUES (1, 1), (2, 1);",
"CALL DOLT_COMMIT('-am', 'data');",
"CALL DOLT_CHECKOUT('right');",
"ALTER TABLE t ADD UNIQUE col1_uniq (col1);",
"CALL DOLT_COMMIT('-am', 'unqiue constraint');",
"CALL DOLT_CHECKOUT('main');",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "CALL DOLT_MERGE('right');",
ExpectedErrStr: "duplicate unique key given: [1]",
},
{
Query: "SELECT * from t",
Expected: []sql.Row{{1, 1}, {2, 1}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t",
"CREATE TABLE `t` (\n" +
" `pk` int NOT NULL,\n" +
" `col1` int,\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
// not a base case, but helpful to understand...
{
Name: "right adds a unique key constraint and fixes existing violations. On merge, because left still has the violation, merge is aborted.",
SetUpScript: []string{
"SET dolt_force_transaction_commit = on;",
"CREATE TABLE t (pk int PRIMARY KEY, col1 int);",
"CALL DOLT_ADD('.');",
"INSERT INTO t VALUES (1, 1), (2, 1);",
"CALL DOLT_COMMIT('-am', 'table and data');",
"CALL DOLT_CHECKOUT('-b', 'right');",
"UPDATE t SET col1 = 2 where pk = 2;",
"ALTER TABLE t ADD UNIQUE col1_uniq (col1);",
"CALL DOLT_COMMIT('-am', 'right adds a unique index');",
"CALL DOLT_CHECKOUT('main');",
"INSERT INTO t VALUES (3, 3);",
"CALL DOLT_COMMIT('-am', 'left edit');",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "CALL DOLT_MERGE('right');",
ExpectedErrStr: "duplicate unique key given: [1]",
},
},
},
}
var GeneratedColumnMergeTestScripts = []queries.ScriptTest{
{
Name: "merge a generated stored column",
@@ -30,7 +30,6 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/sqle"
"github.com/dolthub/dolt/go/store/prolly"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/types"
"github.com/dolthub/dolt/go/store/val"
)
@@ -46,9 +45,6 @@ func ValidateDatabase(ctx context.Context, db sql.Database) (err error) {
}
func ValidateDoltDatabase(ctx context.Context, db sqle.Database) (err error) {
if !types.IsFormat_DOLT(db.GetDoltDB().Format()) {
return nil
}
for _, stage := range validationStages {
if err = stage(ctx, db); err != nil {
return err
@@ -58,13 +58,9 @@ func RowIterForIndexLookup(ctx *sql.Context, t DoltTableable, lookup sql.IndexLo
return nil, err
}
return RowIterForProllyRange(ctx, idx, prollyRanges[0], pkSch, columns, durableState, lookup.IsReverse)
} else {
nomsRanges, err := idx.nomsRanges(ctx, mysqlRanges...)
if err != nil {
return nil, err
}
return RowIterForNomsRanges(ctx, idx, nomsRanges, columns, durableState)
}
panic("Unsupported format for RowIterForIndexLookup")
}
func RowIterForProllyRange(ctx *sql.Context, idx DoltIndex, r prolly.Range, pkSch sql.PrimaryKeySchema, projections []uint64, durableState *durableIndexState, reverse bool) (sql.RowIter, error) {
@@ -85,49 +81,27 @@ func RowIterForProllyRange(ctx *sql.Context, idx DoltIndex, r prolly.Range, pkSc
return newProllyIndexIter(ctx, idx, r, pkSch, projections, durableState.Primary, durableState.Secondary)
}
func RowIterForNomsRanges(ctx *sql.Context, idx DoltIndex, ranges []*noms.ReadRange, columns []uint64, durableState *durableIndexState) (sql.RowIter, error) {
if len(columns) == 0 {
columns = idx.Schema().GetAllCols().Tags
}
m := durable.NomsMapFromIndex(durableState.Secondary)
nrr := noms.NewNomsRangeReader(idx.valueReadWriter(), idx.IndexSchema(), m, ranges)
covers := idx.coversColumns(durableState, columns)
if covers || idx.ID() == "PRIMARY" {
return NewCoveringIndexRowIterAdapter(ctx, idx, nrr, columns), nil
} else {
return NewIndexLookupRowIterAdapter(ctx, idx, durableState, nrr, columns)
}
}
type IndexLookupKeyIterator interface {
// NextKey returns the next key if it exists, and io.EOF if it does not.
NextKey(ctx *sql.Context) (row.TaggedValues, error)
}
func NewRangePartitionIter(ctx *sql.Context, t DoltTableable, lookup sql.IndexLookup, isDoltFmt bool) (sql.PartitionIter, error) {
func NewRangePartitionIter(ctx *sql.Context, t DoltTableable, lookup sql.IndexLookup) (sql.PartitionIter, error) {
mysqlRanges := lookup.Ranges.(sql.MySQLRangeCollection)
idx := lookup.Index.(*doltIndex)
if lookup.IsPointLookup && isDoltFmt {
if lookup.IsPointLookup {
return newPointPartitionIter(ctx, lookup, idx)
}
var prollyRanges []prolly.Range
var nomsRanges []*noms.ReadRange
var err error
if isDoltFmt {
prollyRanges, err = idx.prollyRanges(ctx, idx.ns, mysqlRanges...)
} else {
nomsRanges, err = idx.nomsRanges(ctx, mysqlRanges...)
}
prollyRanges, err = idx.prollyRanges(ctx, idx.ns, mysqlRanges...)
if err != nil {
return nil, err
}
return &rangePartitionIter{
nomsRanges: nomsRanges,
prollyRanges: prollyRanges,
curr: 0,
isDoltFmt: isDoltFmt,
isReverse: lookup.IsReverse,
}, nil
}
@@ -167,10 +141,8 @@ func (p *pointPartition) Next(c *sql.Context) (sql.Partition, error) {
}
type rangePartitionIter struct {
nomsRanges []*noms.ReadRange
prollyRanges []prolly.Range
curr int
isDoltFmt bool
isReverse bool
}
@@ -181,10 +153,7 @@ func (itr *rangePartitionIter) Close(*sql.Context) error {
// Next returns the next partition if there is one, or io.EOF if there isn't.
func (itr *rangePartitionIter) Next(_ *sql.Context) (sql.Partition, error) {
if itr.isDoltFmt {
return itr.nextProllyPartition()
}
return itr.nextNomsPartition()
return itr.nextProllyPartition()
}
func (itr *rangePartitionIter) nextProllyPartition() (sql.Partition, error) {
@@ -204,23 +173,6 @@ func (itr *rangePartitionIter) nextProllyPartition() (sql.Partition, error) {
}, nil
}
func (itr *rangePartitionIter) nextNomsPartition() (sql.Partition, error) {
if itr.curr >= len(itr.nomsRanges) {
return nil, io.EOF
}
var bytes [4]byte
binary.BigEndian.PutUint32(bytes[:], uint32(itr.curr))
nr := itr.nomsRanges[itr.curr]
itr.curr += 1
return rangePartition{
nomsRange: nr,
key: bytes[:],
isReverse: itr.isReverse,
}, nil
}
type rangePartition struct {
nomsRange *noms.ReadRange
key []byte
@@ -310,7 +262,6 @@ func NewIndexReaderBuilder(
key doltdb.DataCacheKey,
projections []uint64,
pkSch sql.PrimaryKeySchema,
isDoltFormat bool,
) (IndexScanBuilder, error) {
if projections == nil {
projections = idx.Schema().GetAllCols().Tags
@@ -328,27 +279,21 @@ func NewIndexReaderBuilder(
projections: projections,
}
if isDoltFormat {
secondaryIndex := durable.MapFromIndex(s.Secondary)
base.ns = secondaryIndex.NodeStore()
base.secKd, base.secVd = secondaryIndex.Descriptors()
base.prefDesc = base.secKd.PrefixDesc(len(di.columns))
switch si := secondaryIndex.(type) {
case prolly.Map:
base.sec = si
case prolly.ProximityMap:
base.proximitySecondary = si
default:
return nil, fmt.Errorf("unknown index type %v", secondaryIndex)
}
secondaryIndex := durable.MapFromIndex(s.Secondary)
base.ns = secondaryIndex.NodeStore()
base.secKd, base.secVd = secondaryIndex.Descriptors()
base.prefDesc = base.secKd.PrefixDesc(len(di.columns))
switch si := secondaryIndex.(type) {
case prolly.Map:
base.sec = si
case prolly.ProximityMap:
base.proximitySecondary = si
default:
return nil, fmt.Errorf("unknown index type %v", secondaryIndex)
}
switch {
case !isDoltFormat:
return &nomsIndexImplBuilder{
baseIndexImplBuilder: base,
s: s,
}, nil
case sql.IsKeyless(pkSch.Schema):
return &keylessIndexImplBuilder{
baseIndexImplBuilder: base,
@@ -412,7 +357,6 @@ func newNonCoveringLookupBuilder(s *durableIndexState, b *baseIndexImplBuilder)
}
var _ IndexScanBuilder = (*baseIndexImplBuilder)(nil)
var _ IndexScanBuilder = (*nomsIndexImplBuilder)(nil)
var _ IndexScanBuilder = (*coveringIndexImplBuilder)(nil)
var _ IndexScanBuilder = (*keylessIndexImplBuilder)(nil)
var _ IndexScanBuilder = (*nonCoveringIndexImplBuilder)(nil)
@@ -875,31 +819,6 @@ func (ib *keylessIndexImplBuilder) NewSecondaryIter(strict bool, cnt int, nullSa
}, nil
}
type nomsIndexImplBuilder struct {
*baseIndexImplBuilder
s *durableIndexState
}
func (ib *nomsIndexImplBuilder) OutputSchema() schema.Schema {
return ib.baseIndexImplBuilder.idx.Schema()
}
// NewPartitionRowIter implements IndexScanBuilder
func (ib *nomsIndexImplBuilder) NewPartitionRowIter(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) {
p := part.(rangePartition)
ranges := []*noms.ReadRange{p.nomsRange}
return RowIterForNomsRanges(ctx, ib.idx, ranges, ib.projections, ib.s)
}
// NewRangeMapIter implements IndexScanBuilder
func (ib *nomsIndexImplBuilder) NewRangeMapIter(ctx context.Context, r prolly.Range, reverse bool) (prolly.MapIter, error) {
panic("cannot call NewMapIter on *nomsIndexImplBuilder")
}
func (ib *nomsIndexImplBuilder) NewSecondaryIter(strict bool, cnt int, nullSafe []bool) (SecondaryLookupIterGen, error) {
panic("cannot call NewSecondaryIter on *nomsIndexImplBuilder")
}
// boundsCase determines the case upon which the bounds are tested.
type boundsCase byte
@@ -1,219 +0,0 @@
// Copyright 2020 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 index_test
import (
"context"
"fmt"
"io"
"testing"
sqle "github.com/dolthub/go-mysql-server"
"github.com/dolthub/go-mysql-server/sql"
"github.com/stretchr/testify/require"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
dsqle "github.com/dolthub/dolt/go/libraries/doltcore/sqle"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
"github.com/dolthub/dolt/go/libraries/doltcore/table/editor"
"github.com/dolthub/dolt/go/store/types"
)
func setupIndexes(t *testing.T, tableName, insertQuery string) (*sqle.Engine, *sql.Context, []*indexTuple) {
ctx := context.Background()
dEnv := dtestutils.CreateTestEnv()
tmpDir, err := dEnv.TempTableFilesDir()
require.NoError(t, err)
deaf, err := dEnv.DbEaFactory(ctx)
require.NoError(t, err)
opts := editor.Options{Deaf: deaf, Tempdir: tmpDir}
db, err := dsqle.NewDatabase(context.Background(), "dolt", dEnv.DbData(ctx), opts)
require.NoError(t, err)
engine, sqlCtx, err := dsqle.NewTestEngine(dEnv, context.Background(), db)
require.NoError(t, err)
_, iter, _, err := engine.Query(sqlCtx, fmt.Sprintf(`CREATE TABLE %s (
pk bigint PRIMARY KEY,
v1 bigint,
v2 bigint,
INDEX idxv1 (v1),
INDEX idxv2v1 (v2,v1)
)`, tableName))
require.NoError(t, err)
require.NoError(t, drainIter(sqlCtx, iter))
_, iter, _, err = engine.Query(sqlCtx, insertQuery)
require.NoError(t, err)
require.NoError(t, drainIter(sqlCtx, iter))
sqlTbl, ok, err := db.GetTableInsensitive(sqlCtx, tableName)
require.NoError(t, err)
require.True(t, ok)
tbl, ok := sqlTbl.(*dsqle.AlterableDoltTable)
require.True(t, ok)
sch := dsqle.DoltSchemaFromAlterableTable(tbl)
idxv1, ok := sch.Indexes().GetByNameCaseInsensitive("idxv1")
require.True(t, ok)
table := dsqle.DoltTableFromAlterableTable(sqlCtx, tbl)
idxv1RowData, err := table.GetIndexRowData(context.Background(), idxv1.Name())
require.NoError(t, err)
idxv1Cols := make([]schema.Column, idxv1.Count())
for i, tag := range idxv1.IndexedColumnTags() {
idxv1Cols[i], _ = idxv1.GetColumn(tag)
}
idxv1ToTuple := &indexTuple{
nbf: idxv1RowData.Format(),
cols: idxv1Cols,
}
idxv2v1, ok := sch.Indexes().GetByNameCaseInsensitive("idxv2v1")
require.True(t, ok)
idxv2v1RowData, err := table.GetNomsIndexRowData(context.Background(), idxv2v1.Name())
require.NoError(t, err)
idxv2v1Cols := make([]schema.Column, idxv2v1.Count())
for i, tag := range idxv2v1.IndexedColumnTags() {
idxv2v1Cols[i], _ = idxv2v1.GetColumn(tag)
}
idxv2v1ToTuple := &indexTuple{
nbf: idxv2v1RowData.Format(),
cols: idxv2v1Cols,
}
mrEnv, err := env.MultiEnvForDirectory(context.Background(), dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
require.NoError(t, err)
b := env.GetDefaultInitBranch(dEnv.Config)
pro, err := dsqle.NewDoltDatabaseProviderWithDatabase(b, mrEnv.FileSystem(), db, dEnv.FS, sql.EngineOverrides{})
if err != nil {
return nil, nil, nil
}
pro = pro.WithDbFactoryUrl(doltdb.InMemDoltDB)
engine = sqle.NewDefault(pro)
it := []*indexTuple{
idxv1ToTuple,
idxv2v1ToTuple,
{
nbf: idxv2v1RowData.Format(),
cols: idxv2v1Cols[:len(idxv2v1Cols)-1],
},
}
return engine, sqlCtx, it
}
// indexTuple converts integers into the appropriate tuple for comparison against ranges
type indexTuple struct {
nbf *types.NomsBinFormat
cols []schema.Column
}
func (it *indexTuple) tuple(vals ...int) types.Tuple {
if len(it.cols) != len(vals) {
panic("len of columns in index does not match the given number of values")
}
valsWithTags := make([]types.Value, len(vals)*2)
for i, val := range vals {
valsWithTags[2*i] = types.Uint(it.cols[i].Tag)
valsWithTags[2*i+1] = types.Int(val)
}
tpl, err := types.NewTuple(it.nbf, valsWithTags...)
if err != nil {
panic(err)
}
return tpl
}
func (it *indexTuple) nilTuple() types.Tuple {
valsWithTags := make([]types.Value, len(it.cols)*2)
for i := 0; i < len(it.cols); i++ {
valsWithTags[2*i] = types.Uint(it.cols[i].Tag)
valsWithTags[2*i+1] = types.NullValue
}
tpl, err := types.NewTuple(it.nbf, valsWithTags...)
if err != nil {
panic(err)
}
return tpl
}
func drainIter(ctx *sql.Context, iter sql.RowIter) error {
for {
_, err := iter.Next(ctx)
if err == io.EOF {
break
} else if err != nil {
closeErr := iter.Close(ctx)
if closeErr != nil {
panic(fmt.Errorf("%v\n%v", err, closeErr))
}
return err
}
}
return iter.Close(ctx)
}
func getDbState(t *testing.T, db sql.Database, dEnv *env.DoltEnv) (dsess.InitialDbState, error) {
ctx := context.Background()
headSpec, err := dEnv.RepoStateReader().CWBHeadSpec(ctx)
if err != nil {
return dsess.InitialDbState{}, err
}
headRef, err := dEnv.RepoStateReader().CWBHeadRef(ctx)
if err != nil {
return dsess.InitialDbState{}, err
}
optCmt, err := dEnv.DoltDB(ctx).Resolve(ctx, headSpec, headRef)
require.NoError(t, err)
headCommit, ok := optCmt.ToCommit()
require.True(t, ok)
ws, err := dEnv.WorkingSet(ctx)
require.NoError(t, err)
return dsess.InitialDbState{
Db: db,
HeadCommit: headCommit,
WorkingSet: ws,
DbData: env.DbData[*sql.Context]{
Ddb: dEnv.DoltDB(ctx),
Rsr: forwardCtxDbData{dEnv.DbData(ctx).Rsr},
Rsw: dEnv.DbData(ctx).Rsw,
},
Remotes: dEnv.RepoState.Remotes,
}, nil
}
type forwardCtxDbData struct {
env.RepoStateReader[context.Context]
}
func (d forwardCtxDbData) CWBHeadSpec(ctx *sql.Context) (*doltdb.CommitSpec, error) {
return d.RepoStateReader.CWBHeadSpec(ctx)
}
func (d forwardCtxDbData) CWBHeadRef(ctx *sql.Context) (ref.DoltRef, error) {
return d.RepoStateReader.CWBHeadRef(ctx)
}
File diff suppressed because it is too large Load Diff
@@ -1,290 +0,0 @@
// Copyright 2020 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 index
import (
"context"
"io"
"sync"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/table/typed/noms"
"github.com/dolthub/dolt/go/libraries/utils/async"
"github.com/dolthub/dolt/go/store/types"
)
const (
ringBufferAllocSize = 1024
)
var resultBufferPool = &sync.Pool{
New: func() interface{} {
return async.NewRingBuffer(ringBufferAllocSize)
},
}
type indexLookupRowIterAdapter struct {
idx DoltIndex
keyIter nomsKeyIter
tableRows types.Map
lookupTags map[uint64]int
conv *KVToSqlRowConverter
resultBuf *async.RingBuffer
cancelF func()
read uint64
count uint64
}
// NewIndexLookupRowIterAdapter returns a new indexLookupRowIterAdapter.
func NewIndexLookupRowIterAdapter(ctx *sql.Context, idx DoltIndex, durableState *durableIndexState, keyIter nomsKeyIter, columns []uint64) (*indexLookupRowIterAdapter, error) {
rows := durable.NomsMapFromIndex(durableState.Primary)
resBuf := resultBufferPool.Get().(*async.RingBuffer)
epoch := resBuf.Reset()
queueCtx, cancelF := context.WithCancel(ctx)
iter := &indexLookupRowIterAdapter{
idx: idx,
keyIter: keyIter,
tableRows: rows,
conv: idx.sqlRowConverter(durableState, columns),
lookupTags: idx.lookupTags(durableState),
cancelF: cancelF,
resultBuf: resBuf,
}
go iter.queueRows(queueCtx, epoch)
return iter, nil
}
// Next returns the next row from the iterator.
func (i *indexLookupRowIterAdapter) Next(ctx *sql.Context) (sql.Row, error) {
for i.count == 0 || i.read < i.count {
item, err := i.resultBuf.Pop()
if err != nil {
return nil, err
}
res := item.(lookupResult)
i.read++
if res.err != nil {
if res.err == io.EOF {
i.count = res.idx
continue
}
return nil, res.err
}
return res.r, res.err
}
return nil, io.EOF
}
func (i *indexLookupRowIterAdapter) Close(*sql.Context) error {
i.cancelF()
resultBufferPool.Put(i.resultBuf)
return nil
}
// queueRows reads each key from the key iterator and writes it to lookups.toLookupCh which manages a pool of worker
// routines which will process the requests in parallel.
func (i *indexLookupRowIterAdapter) queueRows(ctx context.Context, epoch int) {
for idx := uint64(1); ; idx++ {
indexKey, err := i.keyIter.ReadKey(ctx)
if err != nil {
i.resultBuf.Push(lookupResult{
idx: idx,
r: nil,
err: err,
}, epoch)
return
}
lookup := toLookup{
idx: idx,
t: indexKey,
tupleToRow: i.processKey,
resBuf: i.resultBuf,
epoch: epoch,
ctx: ctx,
}
select {
case lookups.toLookupCh <- lookup:
case <-ctx.Done():
err := ctx.Err()
if err == nil {
err = io.EOF
}
i.resultBuf.Push(lookupResult{
idx: idx,
r: nil,
err: err,
}, epoch)
return
}
}
}
func (i *indexLookupRowIterAdapter) indexKeyToTableKey(nbf *types.NomsBinFormat, indexKey types.Tuple) (types.Tuple, error) {
tplItr, err := indexKey.Iterator()
if err != nil {
return types.Tuple{}, err
}
resVals := make([]types.Value, len(i.lookupTags)*2)
for {
_, tag, err := tplItr.NextUint64()
if err != nil {
if err == io.EOF {
break
}
return types.Tuple{}, err
}
idx, inKey := i.lookupTags[tag]
if inKey {
_, valVal, err := tplItr.Next()
if err != nil {
return types.Tuple{}, err
}
resVals[idx*2] = types.Uint(tag)
resVals[idx*2+1] = valVal
} else {
err := tplItr.Skip()
if err != nil {
return types.Tuple{}, err
}
}
}
return types.NewTuple(nbf, resVals...)
}
// processKey is called within queueRows and processes each key, sending the resulting row to the row channel.
func (i *indexLookupRowIterAdapter) processKey(ctx context.Context, indexKey types.Tuple) (sql.Row, error) {
pkTupleVal, err := i.indexKeyToTableKey(i.idx.Format(), indexKey)
if err != nil {
return nil, err
}
fieldsVal, ok, err := i.tableRows.MaybeGetTuple(ctx, pkTupleVal)
if err != nil {
return nil, err
}
if !ok {
return nil, nil
}
sqlRow, err := i.conv.ConvertKVTuplesToSqlRow(pkTupleVal, fieldsVal)
if err != nil {
return nil, err
}
return sqlRow, nil
}
type CoveringIndexRowIterAdapter struct {
idx DoltIndex
rr *noms.NomsRangeReader
conv *KVToSqlRowConverter
ctx *sql.Context
pkCols *schema.ColCollection
nonPKCols *schema.ColCollection
nbf *types.NomsBinFormat
}
func NewCoveringIndexRowIterAdapter(ctx *sql.Context, idx DoltIndex, keyIter *noms.NomsRangeReader, resultCols []uint64) *CoveringIndexRowIterAdapter {
idxCols := idx.IndexSchema().GetPKCols()
tblPKCols := idx.Schema().GetPKCols()
sch := idx.Schema()
allCols := sch.GetAllCols().GetColumns()
cols := make([]schema.Column, 0)
for _, col := range allCols {
for _, tag := range resultCols {
if col.Tag == tag {
cols = append(cols, col)
}
}
}
tagToSqlColIdx := make(map[uint64]int)
isPrimaryKeyIdx := idx.ID() == "PRIMARY"
resultColSet := make(map[uint64]bool)
for _, k := range resultCols {
resultColSet[k] = true
}
for i, col := range cols {
_, partOfIdxKey := idxCols.GetByNameCaseInsensitive(col.Name)
// Either this is a primary key index or the key is a part of the index and this part of the result column set.
if (partOfIdxKey || isPrimaryKeyIdx) && (len(resultCols) == 0 || resultColSet[col.Tag]) {
tagToSqlColIdx[col.Tag] = i
}
}
// TAGS: Use of tags here is safe since it's constrained to a single table
for i, col := range cols {
_, partOfIndexKey := idxCols.GetByTag(col.Tag)
_, partOfTableKeys := tblPKCols.GetByTag(col.Tag)
if partOfIndexKey != partOfTableKeys {
cols[i], _ = schema.NewColumnWithTypeInfo(col.Name, col.Tag, col.TypeInfo, partOfIndexKey, col.Default, col.AutoIncrement, col.Comment, col.Constraints...)
}
}
return &CoveringIndexRowIterAdapter{
idx: idx,
rr: keyIter,
conv: NewKVToSqlRowConverter(idx.Format(), tagToSqlColIdx, cols, len(cols)),
ctx: ctx,
pkCols: sch.GetPKCols(),
nonPKCols: sch.GetNonPKCols(),
nbf: idx.Format(),
}
}
// Next returns the next row from the iterator.
func (ci *CoveringIndexRowIterAdapter) Next(ctx *sql.Context) (sql.Row, error) {
key, value, err := ci.rr.ReadKV(ctx)
if err != nil {
return nil, err
}
return ci.conv.ConvertKVTuplesToSqlRow(key, value)
}
func (ci *CoveringIndexRowIterAdapter) Close(*sql.Context) error {
return nil
}
@@ -20,7 +20,6 @@ import (
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
"github.com/dolthub/dolt/go/store/types"
)
// IndexedDoltTable is a wrapper for a DoltTable. It implements the sql.Table interface like
@@ -30,16 +29,14 @@ type IndexedDoltTable struct {
idx index.DoltIndex
lb index.IndexScanBuilder
*DoltTable
mu *sync.Mutex
isDoltFormat bool
mu *sync.Mutex
}
func NewIndexedDoltTable(t *DoltTable, idx index.DoltIndex) *IndexedDoltTable {
return &IndexedDoltTable{
DoltTable: t,
idx: idx,
isDoltFormat: types.IsFormat_DOLT(t.Format()),
mu: &sync.Mutex{},
DoltTable: t,
idx: idx,
mu: &sync.Mutex{},
}
}
@@ -58,13 +55,13 @@ func (t *IndexedDoltTable) LookupBuilder(ctx *sql.Context) (index.IndexScanBuild
return nil, err
}
if t.lb == nil || !canCache || t.lb.Key() != key {
return index.NewIndexReaderBuilder(ctx, t.DoltTable, t.idx, key, t.DoltTable.projectedCols, t.DoltTable.sqlSch, t.isDoltFormat)
return index.NewIndexReaderBuilder(ctx, t.DoltTable, t.idx, key, t.DoltTable.projectedCols, t.DoltTable.sqlSch)
}
return t.lb, nil
}
func (idt *IndexedDoltTable) LookupPartitions(ctx *sql.Context, lookup sql.IndexLookup) (sql.PartitionIter, error) {
return index.NewRangePartitionIter(ctx, idt.DoltTable, lookup, idt.isDoltFormat)
return index.NewRangePartitionIter(ctx, idt.DoltTable, lookup)
}
func (idt *IndexedDoltTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
@@ -80,7 +77,7 @@ func (idt *IndexedDoltTable) PartitionRows(ctx *sql.Context, part sql.Partition)
}
if idt.lb == nil || !canCache || idt.lb.Key() != key {
idt.lb, err = index.NewIndexReaderBuilder(ctx, idt.DoltTable, idt.idx, key, idt.DoltTable.projectedCols, idt.DoltTable.sqlSch, idt.isDoltFormat)
idt.lb, err = index.NewIndexReaderBuilder(ctx, idt.DoltTable, idt.idx, key, idt.DoltTable.projectedCols, idt.DoltTable.sqlSch)
if err != nil {
return nil, err
}
@@ -97,7 +94,7 @@ func (idt *IndexedDoltTable) PartitionRows2(ctx *sql.Context, part sql.Partition
return nil, err
}
if idt.lb == nil || !canCache || idt.lb.Key() != key {
idt.lb, err = index.NewIndexReaderBuilder(ctx, idt.DoltTable, idt.idx, key, idt.DoltTable.projectedCols, idt.DoltTable.sqlSch, idt.isDoltFormat)
idt.lb, err = index.NewIndexReaderBuilder(ctx, idt.DoltTable, idt.idx, key, idt.DoltTable.projectedCols, idt.DoltTable.sqlSch)
if err != nil {
return nil, err
}
@@ -117,7 +114,6 @@ func NewWritableIndexedDoltTable(t *WritableDoltTable, idx index.DoltIndex) *Wri
return &WritableIndexedDoltTable{
WritableDoltTable: t,
idx: idx,
isDoltFormat: types.IsFormat_DOLT(idx.Format()),
mu: &sync.Mutex{},
}
}
@@ -126,8 +122,7 @@ type WritableIndexedDoltTable struct {
idx index.DoltIndex
lb index.IndexScanBuilder
*WritableDoltTable
mu *sync.Mutex
isDoltFormat bool
mu *sync.Mutex
}
func (t *WritableIndexedDoltTable) Index() index.DoltIndex {
@@ -142,7 +137,7 @@ func (t *WritableIndexedDoltTable) LookupBuilder(ctx *sql.Context) (index.IndexS
return nil, err
}
if t.lb == nil || !canCache || t.lb.Key() != key {
return index.NewIndexReaderBuilder(ctx, t.DoltTable, t.idx, key, t.DoltTable.projectedCols, t.DoltTable.sqlSch, t.isDoltFormat)
return index.NewIndexReaderBuilder(ctx, t.DoltTable, t.idx, key, t.DoltTable.projectedCols, t.DoltTable.sqlSch)
}
return t.lb, nil
}
@@ -151,7 +146,7 @@ func (t *WritableIndexedDoltTable) LookupPartitions(ctx *sql.Context, lookup sql
if lookup.VectorOrderAndLimit.OrderBy != nil {
return index.NewVectorPartitionIter(lookup)
}
return index.NewRangePartitionIter(ctx, t.DoltTable, lookup, t.isDoltFormat)
return index.NewRangePartitionIter(ctx, t.DoltTable, lookup)
}
func (t *WritableIndexedDoltTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
@@ -166,7 +161,7 @@ func (t *WritableIndexedDoltTable) PartitionRows(ctx *sql.Context, part sql.Part
return nil, err
}
if t.lb == nil || !canCache || t.lb.Key() != key {
t.lb, err = index.NewIndexReaderBuilder(ctx, t.DoltTable, t.idx, key, t.projectedCols, t.sqlSch, t.isDoltFormat)
t.lb, err = index.NewIndexReaderBuilder(ctx, t.DoltTable, t.idx, key, t.projectedCols, t.sqlSch)
if err != nil {
return nil, err
}
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -397,7 +397,7 @@ func getSourceKv(ctx *sql.Context, n sql.Node, isSrc bool) (prolly.Map, prolly.M
return prolly.Map{}, nil, nil, nil, nil, nil, err
}
if rowData.Format() != types.Format_DOLT {
return prolly.Map{}, nil, nil, nil, nil, nil, nil
panic("Unsupported index format in lookup join: " + rowData.Format().VersionString())
}
priMap, err = durable.ProllyMapFromIndex(rowData)
if err != nil {
@@ -568,7 +568,7 @@ func getMergeKv(ctx *sql.Context, n sql.Node) (mergeState, error) {
}
if idx.Format() != types.Format_DOLT {
return ms, nil
panic("Unsupported index format in merge join: " + idx.Format().VersionString())
}
secIdx, err := index.GetDurableIndex(ctx, doltTable, idx)
+2 -172
View File
@@ -23,8 +23,6 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
"github.com/dolthub/dolt/go/libraries/doltcore/table"
"github.com/dolthub/dolt/go/store/types"
)
@@ -58,13 +56,6 @@ func (k keylessRowIter) Close(ctx *sql.Context) error {
return k.keyedIter.Close(ctx)
}
// An iterator over the rows of a table.
type doltTableRowIter struct {
sql.RowIter
ctx context.Context
reader table.SqlTableReader
}
// Returns a new row iterator for the table given
func newRowIterator(ctx context.Context, tbl *doltdb.Table, projCols []uint64, partition doltTablePartition) (sql.RowIter, error) {
sch, err := tbl.GetSchema(ctx)
@@ -73,106 +64,8 @@ func newRowIterator(ctx context.Context, tbl *doltdb.Table, projCols []uint64, p
return nil, err
}
if types.IsFormat_DOLT(tbl.Format()) {
return ProllyRowIterFromPartition(ctx, sch, projCols, partition)
}
mapIter, err := iterForPartition(ctx, partition)
if err != nil {
return nil, err
}
if schema.IsKeyless(sch) {
// would be more optimal to project columns into keyless tables also
return newKeylessRowIterator(ctx, tbl, projCols, mapIter)
} else {
return newKeyedRowIter(ctx, tbl, projCols, mapIter)
}
}
func newKeylessRowIterator(ctx context.Context, tbl *doltdb.Table, projectedCols []uint64, mapIter types.MapTupleIterator) (sql.RowIter, error) {
cols, tagToSqlColIdx, err := getTagToResColIdx(ctx, tbl, projectedCols)
if err != nil {
return nil, err
}
idxOfCardinality := len(cols)
tagToSqlColIdx[schema.KeylessRowCardinalityTag] = idxOfCardinality
colsCopy := make([]schema.Column, len(cols), len(cols)+1)
copy(colsCopy, cols)
colsCopy = append(colsCopy, schema.NewColumn("__cardinality__", schema.KeylessRowCardinalityTag, types.UintKind, false))
conv := index.NewKVToSqlRowConverter(tbl.Format(), tagToSqlColIdx, colsCopy, len(colsCopy))
keyedItr, err := index.NewDoltMapIter(mapIter.NextTuple, nil, conv), nil
if err != nil {
return nil, err
}
return &keylessRowIter{
keyedIter: keyedItr,
cardIdx: idxOfCardinality,
nonCardCols: len(cols),
}, nil
}
func newKeyedRowIter(ctx context.Context, tbl *doltdb.Table, projectedCols []uint64, mapIter types.MapTupleIterator) (sql.RowIter, error) {
cols, tagToSqlColIdx, err := getTagToResColIdx(ctx, tbl, projectedCols)
if err != nil {
return nil, err
}
conv := index.NewKVToSqlRowConverter(tbl.Format(), tagToSqlColIdx, cols, len(cols))
return index.NewDoltMapIter(mapIter.NextTuple, nil, conv), nil
}
func iterForPartition(ctx context.Context, partition doltTablePartition) (types.MapTupleIterator, error) {
if partition.end == NoUpperBound {
c, err := partition.rowData.Count()
if err != nil {
return nil, err
}
partition.end = c
}
return partition.IteratorForPartition(ctx, partition.rowData)
}
func getTagToResColIdx(ctx context.Context, tbl *doltdb.Table, projectedCols []uint64) ([]schema.Column, map[uint64]int, error) {
sch, err := tbl.GetSchema(ctx)
if err != nil {
return nil, nil, err
}
allCols := sch.GetAllCols().GetColumns()
tagToSqlColIdx := make(map[uint64]int)
if projectedCols != nil {
outCols := make([]schema.Column, len(projectedCols))
for i := range projectedCols {
t := projectedCols[i]
idx := sch.GetAllCols().TagToIdx[t]
tagToSqlColIdx[t] = i
outCols[i] = allCols[idx]
}
return outCols, tagToSqlColIdx, nil
}
for i, col := range allCols {
tagToSqlColIdx[col.Tag] = i
}
return allCols, tagToSqlColIdx, nil
}
// Next returns the next row in this row iterator, or an io.EOF error if there aren't any more.
func (itr *doltTableRowIter) Next() (sql.Row, error) {
return itr.reader.ReadSqlRow(itr.ctx)
}
// Close required by sql.RowIter interface
func (itr *doltTableRowIter) Close(*sql.Context) error {
return nil
types.AssertFormat_DOLT(tbl.Format())
return ProllyRowIterFromPartition(ctx, sch, projCols, partition)
}
func ProllyRowIterFromPartition(
@@ -227,66 +120,3 @@ func SqlTableToRowIter(ctx *sql.Context, table *DoltTable, columns []uint64) (sq
return newRowIterator(ctx, t, columns, p)
}
// DoltTablePartitionToRowIter returns a sql.RowIter for a partition of the clustered index of |table|.
func DoltTablePartitionToRowIter(ctx *sql.Context, name string, table *doltdb.Table, start uint64, end uint64) (sql.Schema, sql.RowIter, error) {
sch, err := table.GetSchema(ctx)
if err != nil {
return nil, nil, err
}
pkSch, err := sqlutil.FromDoltSchema("", name, sch)
if err != nil {
return nil, nil, err
}
data, err := table.GetRowData(ctx)
if err != nil {
return nil, nil, err
}
if types.IsFormat_DOLT(data.Format()) {
idx, err := durable.ProllyMapFromIndex(data)
if err != nil {
return nil, nil, err
}
c, err := idx.Count()
if err != nil {
return nil, nil, err
}
if end > uint64(c) {
end = uint64(c)
}
iter, err := idx.IterOrdinalRange(ctx, start, end)
if err != nil {
return nil, nil, err
}
rowIter := index.NewProllyRowIterForMap(sch, idx, iter, nil)
if err != nil {
return nil, nil, err
}
return pkSch.Schema, rowIter, nil
}
idx := durable.NomsMapFromIndex(data)
iterAt, err := idx.IteratorAt(ctx, start)
if err != nil {
return nil, nil, err
}
iter := types.NewLimitingMapIterator(iterAt, end-start)
var rowIter sql.RowIter
if schema.IsKeyless(sch) {
rowIter, err = newKeylessRowIterator(ctx, table, nil, iter)
if err != nil {
return nil, nil, err
}
} else {
rowIter, err = newKeyedRowIter(ctx, table, nil, iter)
if err != nil {
return nil, nil, err
}
}
return pkSch.Schema, rowIter, nil
}
+9 -450
View File
@@ -32,12 +32,10 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/gcctx"
"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/writer"
"github.com/dolthub/dolt/go/store/types"
)
func TestCreateTable(t *testing.T) {
@@ -154,15 +152,15 @@ func TestCreateTable(t *testing.T) {
schemaNewColumn(t, "c10", 1965, gmstypes.TinyText, false),
schemaNewColumn(t, "c11", 12860, gmstypes.MediumText, false),
schemaNewColumn(t, "c12", 7155, gmstypes.LongText, false),
//schemaNewColumn(t, "c13", 113, sql.TinyBlob, false),
//schemaNewColumn(t, "c14", 114, sql.Blob, false),
//schemaNewColumn(t, "c15", 115, sql.LongBlob, false),
// schemaNewColumn(t, "c13", 113, sql.TinyBlob, false),
// schemaNewColumn(t, "c14", 114, sql.Blob, false),
// schemaNewColumn(t, "c15", 115, sql.LongBlob, false),
schemaNewColumn(t, "c16", 15859, gmstypes.MustCreateStringWithDefaults(sqltypes.Char, 5), false),
schemaNewColumn(t, "c17", 11710, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 255), false),
schemaNewColumn(t, "c18", 6838, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false),
schemaNewColumn(t, "c19", 9377, gmstypes.Float32, false),
schemaNewColumn(t, "c20", 15979, gmstypes.Float64, false),
//schemaNewColumn(t, "c21", 121, sql.MustCreateDecimalType(10, 5), false),
// schemaNewColumn(t, "c21", 121, sql.MustCreateDecimalType(10, 5), false),
schemaNewColumn(t, "c22", 2910, gmstypes.Uint32, false),
schemaNewColumn(t, "c23", 8740, gmstypes.Uint8, false),
schemaNewColumn(t, "c24", 8689, gmstypes.Uint16, false),
@@ -366,445 +364,6 @@ func TestDropTable(t *testing.T) {
}
}
func TestAddColumn(t *testing.T) {
tests := []struct {
expectedSchema schema.Schema
name string
query string
expectedErr string
expectedRows []row.Row
}{
{
name: "alter add string column no default",
query: "alter table people add (newColumn varchar(80))",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumn(t, "newColumn", 4208, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false)),
expectedRows: addColToRows(t, AllPeopleRows, 4208, nil),
},
{
name: "alter add float column without default",
query: "alter table people add (newColumn float)",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumn(t, "newColumn", 4208, gmstypes.Float32, false)),
expectedRows: addColToRows(t, AllPeopleRows, 4208, nil),
},
{
name: "alter add uint column without default",
query: "alter table people add (newColumn bigint unsigned)",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumn(t, "newColumn", 4208, gmstypes.Uint64, false)),
expectedRows: addColToRows(t, AllPeopleRows, 4208, nil),
},
{
name: "alter add int column default",
query: "alter table people add (newColumn int default 2)",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumnWDefVal(t, "newColumn", 2803, gmstypes.Int32, false, "2")),
expectedRows: addColToRows(t, AllPeopleRows, 2803, types.Int(int32(2))),
},
{
name: "alter add uint column default",
query: "alter table people add (newColumn bigint unsigned default 20)",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumnWDefVal(t, "newColumn", 517, gmstypes.Uint64, false, "20")),
expectedRows: addColToRows(t, AllPeopleRows, 517, types.Uint(uint64(20))),
},
{
name: "alter add string column with default",
query: "alter table people add (newColumn varchar(80) default 'hi')",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumnWDefVal(t, "newColumn", 13690, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, `'hi'`)),
expectedRows: addColToRows(t, AllPeopleRows, 13690, types.String("hi")),
},
{
name: "alter add column first",
query: "alter table people add newColumn varchar(80) first",
expectedSchema: dtestutils.CreateSchema(
schemaNewColumn(t, "newColumn", 4208, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false),
schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}),
schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("is_married", IsMarriedTag, types.IntKind, false),
schema.NewColumn("age", AgeTag, types.IntKind, false),
schema.NewColumn("rating", RatingTag, types.FloatKind, false),
schema.NewColumn("uuid", UuidTag, types.StringKind, false),
schema.NewColumn("num_episodes", NumEpisodesTag, types.UintKind, false),
),
expectedRows: addColToRows(t, AllPeopleRows, 4208, nil),
},
{
name: "alter add column middle",
query: "alter table people add newColumn varchar(80) after last_name",
expectedSchema: dtestutils.CreateSchema(
schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}),
schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schemaNewColumn(t, "newColumn", 4208, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false),
schema.NewColumn("is_married", IsMarriedTag, types.IntKind, false),
schema.NewColumn("age", AgeTag, types.IntKind, false),
schema.NewColumn("rating", RatingTag, types.FloatKind, false),
schema.NewColumn("uuid", UuidTag, types.StringKind, false),
schema.NewColumn("num_episodes", NumEpisodesTag, types.UintKind, false),
),
expectedRows: addColToRows(t, AllPeopleRows, 4208, nil),
},
{
name: "alter add column not null",
query: "alter table people add (newColumn varchar(80) not null default 'default')",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumnWDefVal(t, "newColumn", 13690, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, `'default'`, schema.NotNullConstraint{})),
expectedRows: addColToRows(t, AllPeopleRows, 13690, types.String("default")),
},
{
name: "alter add column not null with expression default",
query: "alter table people add (newColumn int not null default (2+2/2))",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumnWDefVal(t, "newColumn", 2803, gmstypes.Int32, false, "((2 + (2 / 2)))", schema.NotNullConstraint{})),
expectedRows: addColToRows(t, AllPeopleRows, 2803, types.Int(3)),
},
{
name: "alter add column not null with negative expression",
query: "alter table people add (newColumn float not null default -1.1)",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumnWDefVal(t, "newColumn", 12469, gmstypes.Float32, false, "-1.1", schema.NotNullConstraint{})),
expectedRows: addColToRows(t, AllPeopleRows, 12469, types.Float(float32(-1.1))),
},
{
name: "alter add column not null with type mismatch in default",
query: "alter table people add (newColumn float not null default 'not a number')",
expectedErr: "incompatible type",
},
{
name: "alter add column column not found",
query: "alter table people add column newColumn float after notFound",
expectedErr: `table "people" does not have column "notFound"`,
},
{
name: "alter add column table not found",
query: "alter table notFound add column newColumn float",
expectedErr: "table not found: notFound",
},
{
name: "alter add column not null without default",
query: "alter table people add (newColumn varchar(80) not null)",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumnWDefVal(t, "newColumn", 13690, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, "", schema.NotNullConstraint{})),
expectedRows: addColToRows(t, AllPeopleRows, 13690, types.String("")),
},
{
name: "alter add column nullable",
query: "alter table people add (newColumn bigint)",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumn(t, "newColumn", 4435, gmstypes.Int64, false)),
expectedRows: AllPeopleRows,
},
{
name: "alter add column with optional column keyword",
query: "alter table people add column (newColumn varchar(80))",
expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema,
schemaNewColumn(t, "newColumn", 4208, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false)),
expectedRows: AllPeopleRows,
},
{
name: "alter table add column name clash",
query: "alter table people add column(age int)",
expectedErr: `Column "age" already exists`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
dEnv, err := CreateTestDatabase()
require.NoError(t, err)
defer dEnv.DoltDB(ctx).Close()
root, err := dEnv.WorkingRoot(ctx)
require.NoError(t, err)
updatedRoot, err := ExecuteSql(ctx, dEnv, root, tt.query)
if tt.expectedErr == "" {
require.NoError(t, err)
} else {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.expectedErr)
return
}
assert.NotNil(t, updatedRoot)
table, _, err := updatedRoot.GetTable(ctx, doltdb.TableName{Name: PeopleTableName})
assert.NoError(t, err)
sch, err := table.GetSchema(ctx)
assert.NoError(t, err)
equalSchemas(t, tt.expectedSchema, sch)
if types.Format_Default != types.Format_LD_1 {
return // todo: convert these to enginetests
}
updatedTable, ok, err := updatedRoot.GetTable(ctx, doltdb.TableName{Name: "people"})
assert.NoError(t, err)
require.True(t, ok)
rowData, err := updatedTable.GetNomsRowData(ctx)
assert.NoError(t, err)
var foundRows []row.Row
err = rowData.Iter(ctx, func(key, value types.Value) (stop bool, err error) {
r, err := row.FromNoms(tt.expectedSchema, key.(types.Tuple), value.(types.Tuple))
assert.NoError(t, err)
foundRows = append(foundRows, r)
return false, nil
})
assert.NoError(t, err)
assert.Equal(t, tt.expectedRows, foundRows)
})
}
}
func TestRenameColumn(t *testing.T) {
tests := []struct {
expectedSchema schema.Schema
name string
query string
expectedErr string
expectedRows []row.Row
}{
{
name: "alter rename column with column and as keywords",
query: "alter table people rename column rating as newRating",
expectedSchema: dtestutils.CreateSchema(
schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}),
schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("is_married", IsMarriedTag, types.IntKind, false),
schema.NewColumn("age", AgeTag, types.IntKind, false),
schema.NewColumn("newRating", RatingTag, types.FloatKind, false),
schema.NewColumn("uuid", UuidTag, types.StringKind, false),
schema.NewColumn("num_episodes", NumEpisodesTag, types.UintKind, false),
),
expectedRows: AllPeopleRows,
},
{
name: "alter rename column with column and to keyword",
query: "alter table people rename column rating to newRating",
expectedSchema: dtestutils.CreateSchema(
schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}),
schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("is_married", IsMarriedTag, types.IntKind, false),
schema.NewColumn("age", AgeTag, types.IntKind, false),
schema.NewColumn("newRating", RatingTag, types.FloatKind, false),
schema.NewColumn("uuid", UuidTag, types.StringKind, false),
schema.NewColumn("num_episodes", NumEpisodesTag, types.UintKind, false),
),
expectedRows: AllPeopleRows,
},
{
name: "alter rename primary key column",
query: "alter table people rename column id to newId",
expectedSchema: dtestutils.CreateSchema(
schema.NewColumn("newId", IdTag, types.IntKind, true, schema.NotNullConstraint{}),
schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("is_married", IsMarriedTag, types.IntKind, false),
schema.NewColumn("age", AgeTag, types.IntKind, false),
schema.NewColumn("rating", RatingTag, types.FloatKind, false),
schema.NewColumn("uuid", UuidTag, types.StringKind, false),
schema.NewColumn("num_episodes", NumEpisodesTag, types.UintKind, false),
),
expectedRows: AllPeopleRows,
},
{
name: "table not found",
query: "alter table notFound rename column id to newId",
expectedErr: "table not found: notFound",
},
{
name: "column not found",
query: "alter table people rename column notFound to newNotFound",
expectedErr: `table "people" does not have column "notFound"`,
},
{
name: "column name collision",
query: "alter table people rename column id to AGE",
expectedErr: "Column \"AGE\" already exists",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
dEnv, err := CreateTestDatabase()
require.NoError(t, err)
defer dEnv.DoltDB(ctx).Close()
root, _ := dEnv.WorkingRoot(ctx)
updatedRoot, err := ExecuteSql(ctx, dEnv, root, tt.query)
if tt.expectedErr == "" {
require.NoError(t, err)
} else {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.expectedErr)
return
}
require.NotNil(t, updatedRoot)
table, _, err := updatedRoot.GetTable(ctx, doltdb.TableName{Name: PeopleTableName})
assert.NoError(t, err)
sch, err := table.GetSchema(ctx)
require.NoError(t, err)
assert.Equal(t, tt.expectedSchema, sch)
if types.Format_Default != types.Format_LD_1 {
return // todo: convert these to enginetests
}
updatedTable, ok, err := updatedRoot.GetTable(ctx, doltdb.TableName{Name: "people"})
assert.NoError(t, err)
require.True(t, ok)
rowData, err := updatedTable.GetNomsRowData(ctx)
assert.NoError(t, err)
var foundRows []row.Row
err = rowData.Iter(ctx, func(key, value types.Value) (stop bool, err error) {
updatedSch, err := updatedTable.GetSchema(ctx)
assert.NoError(t, err)
r, err := row.FromNoms(updatedSch, key.(types.Tuple), value.(types.Tuple))
assert.NoError(t, err)
foundRows = append(foundRows, r)
return false, nil
})
assert.NoError(t, err)
assert.Equal(t, tt.expectedRows, foundRows)
})
}
}
func TestRenameTableStatements(t *testing.T) {
tests := []struct {
expectedSchema schema.Schema
name string
query string
oldTableName string
newTableName string
expectedErr string
expectedRows []row.Row
}{
{
name: "alter rename table",
query: "rename table people to newPeople",
oldTableName: "people",
newTableName: "newPeople",
expectedSchema: PeopleTestSchema,
expectedRows: AllPeopleRows,
},
{
name: "alter rename table with alter syntax",
query: "alter table people rename to newPeople",
oldTableName: "people",
newTableName: "newPeople",
expectedSchema: PeopleTestSchema,
expectedRows: AllPeopleRows,
},
{
name: "rename multiple tables",
query: "rename table people to newPeople, appearances to newAppearances",
oldTableName: "appearances",
newTableName: "newAppearances",
expectedSchema: AppearancesTestSchema,
expectedRows: AllAppsRows,
},
{
name: "alter rename table with alter syntax",
query: "alter table people rename to 123People",
oldTableName: "people",
newTableName: "123People",
expectedSchema: PeopleTestSchema,
expectedRows: AllPeopleRows,
},
{
name: "table not found",
query: "rename table notFound to newNowFound",
expectedErr: "table not found: notFound",
},
{
name: "invalid table name",
query: "rename table people to `a!trailing^space*is%the(worst) `",
expectedErr: "Invalid table name",
},
{
name: "reserved table name",
query: "rename table people to dolt_table",
expectedErr: "Invalid table name",
},
{
name: "table name in use",
query: "rename table people to appearances",
expectedErr: "already exists",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
dEnv, err := CreateTestDatabase()
require.NoError(t, err)
defer dEnv.DoltDB(ctx).Close()
root, err := dEnv.WorkingRoot(ctx)
require.NoError(t, err)
updatedRoot, err := ExecuteSql(ctx, dEnv, root, tt.query)
if len(tt.expectedErr) > 0 {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.expectedErr)
return
} else {
require.NoError(t, err)
}
require.NotNil(t, updatedRoot)
has, err := updatedRoot.HasTable(ctx, doltdb.TableName{Name: tt.oldTableName})
require.NoError(t, err)
assert.False(t, has)
newTable, ok, err := updatedRoot.GetTable(ctx, doltdb.TableName{Name: tt.newTableName})
require.NoError(t, err)
require.True(t, ok)
sch, err := newTable.GetSchema(ctx)
require.NoError(t, err)
require.Equal(t, tt.expectedSchema, sch)
if types.Format_Default != types.Format_LD_1 {
return // todo: convert these to enginetests
}
rowData, err := newTable.GetNomsRowData(ctx)
require.NoError(t, err)
var foundRows []row.Row
err = rowData.Iter(ctx, func(key, value types.Value) (stop bool, err error) {
r, err := row.FromNoms(tt.expectedSchema, key.(types.Tuple), value.(types.Tuple))
require.NoError(t, err)
foundRows = append(foundRows, r)
return false, nil
})
require.NoError(t, err)
// Some test cases deal with rows declared in a different order than noms returns them, so use an order-
// insensitive comparison here.
assert.ElementsMatch(t, tt.expectedRows, foundRows)
})
}
}
func TestAlterSystemTables(t *testing.T) {
systemTableNames := []string{"dolt_log", "dolt_history_people", "dolt_diff_people", "dolt_commit_diff_people", "dolt_schemas"}
reservedTableNames := []string{"dolt_query_catalog", "dolt_docs", "dolt_procedures", "dolt_ignore"}
@@ -967,15 +526,15 @@ func TestParseCreateTableStatement(t *testing.T) {
schemaNewColumn(t, "c10", 1965, gmstypes.TinyText, false),
schemaNewColumn(t, "c11", 12860, gmstypes.MediumText, false),
schemaNewColumn(t, "c12", 7155, gmstypes.LongText, false),
//schemaNewColumn(t, "c13", 113, sql.TinyBlob, false),
//schemaNewColumn(t, "c14", 114, sql.Blob, false),
//schemaNewColumn(t, "c15", 115, sql.LongBlob, false),
// schemaNewColumn(t, "c13", 113, sql.TinyBlob, false),
// schemaNewColumn(t, "c14", 114, sql.Blob, false),
// schemaNewColumn(t, "c15", 115, sql.LongBlob, false),
schemaNewColumn(t, "c16", 15859, gmstypes.MustCreateStringWithDefaults(sqltypes.Char, 5), false),
schemaNewColumn(t, "c17", 11710, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 255), false),
schemaNewColumn(t, "c18", 6838, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false),
schemaNewColumn(t, "c19", 9377, gmstypes.Float32, false),
schemaNewColumn(t, "c20", 15979, gmstypes.Float64, false),
//schemaNewColumn(t, "c21", 121, sql.MustCreateDecimalType(10, 5), false),
// schemaNewColumn(t, "c21", 121, sql.MustCreateDecimalType(10, 5), false),
schemaNewColumn(t, "c22", 2910, gmstypes.Uint32, false),
schemaNewColumn(t, "c23", 8740, gmstypes.Uint8, false),
schemaNewColumn(t, "c24", 8689, gmstypes.Uint16, false),
@@ -1078,7 +637,7 @@ func TestParseCreateTableStatement(t *testing.T) {
dEnv := dtestutils.CreateTestEnv()
defer dEnv.DoltDB(ctx).Close()
root, _ := dEnv.WorkingRoot(ctx)
//eng, dbName, _ := engine.NewSqlEngineForEnv(ctx, dEnv)
// eng, dbName, _ := engine.NewSqlEngineForEnv(ctx, dEnv)
eng, sqlCtx := newTestEngine(ctx, dEnv)
_, iter, _, err := eng.Query(sqlCtx, "create database test")
+2 -181
View File
@@ -25,10 +25,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
@@ -37,7 +35,7 @@ import (
)
// Set to the name of a single test to run just that test, useful for debugging
const singleSelectQueryTest = "" //"Natural join with join clause"
const singleSelectQueryTest = "" // "Natural join with join clause"
// Set to false to run tests known to be broken
const skipBrokenSelect = true
@@ -1270,18 +1268,6 @@ func TestSelect(t *testing.T) {
}
}
func TestAsOfQueries(t *testing.T) {
if types.Format_Default != types.Format_LD_1 {
t.Skip("") // todo: convert to enginetests
}
for _, test := range AsOfTests {
t.Run(test.Name, func(t *testing.T) {
// AS OF queries use the same history as the diff tests, so exercise the same test setup
testSelectDiffQuery(t, test)
})
}
}
func TestJoins(t *testing.T) {
for _, tt := range JoinTests {
if tt.Name == "Join from table with two key columns to table with one key column" {
@@ -1379,7 +1365,7 @@ func testSelectQuery(t *testing.T, test SelectTest) {
expectedRows := unwrapRows(t, test.ExpectedRows)
actualRows = unwrapRows(t, actualRows)
// JSON columns must be compared using like so
assert.Equal(t, len(expectedRows), len(actualRows))
require.Equal(t, len(expectedRows), len(actualRows))
for i := 0; i < len(expectedRows); i++ {
assert.Equal(t, len(expectedRows[i]), len(actualRows[i]))
for j := 0; j < len(expectedRows[i]); j++ {
@@ -1548,63 +1534,6 @@ var DiffSchema = dtestutils.MustSchema(
schema.NewColumn("diff_type", 14, types.StringKind, false),
)
func testSelectDiffQuery(t *testing.T, test SelectTest) {
validateTest(t, test)
ctx := context.Background()
cleanup := installTestCommitClock()
defer cleanup()
dEnv := dtestutils.CreateTestEnv()
initializeWithHistory(t, ctx, dEnv, CreateHistory(ctx, dEnv, t)...)
if test.AdditionalSetup != nil {
test.AdditionalSetup(t, dEnv)
}
cs, err := doltdb.NewCommitSpec("main")
require.NoError(t, err)
optCmt, err := dEnv.DoltDB(ctx).Resolve(ctx, cs, nil)
require.NoError(t, err)
cm, ok := optCmt.ToCommit()
require.True(t, ok)
root, err := cm.GetRootValue(ctx)
require.NoError(t, err)
err = dEnv.UpdateStagedRoot(ctx, root)
require.NoError(t, err)
err = dEnv.UpdateWorkingRoot(ctx, root)
require.NoError(t, err)
root, err = dEnv.WorkingRoot(context.Background())
require.NoError(t, err)
root = updateTables(t, ctx, root, createWorkingRootUpdate())
err = dEnv.UpdateWorkingRoot(ctx, root)
require.NoError(t, err)
actualRows, sch, err := executeSelect(t, ctx, dEnv, root, test.Query)
if len(test.ExpectedErr) > 0 {
require.Error(t, err)
return
} else {
require.NoError(t, err)
}
assert.Equal(t, test.ExpectedRows, actualRows)
var sqlSchema sql.Schema
if test.ExpectedSqlSchema != nil {
sqlSchema = test.ExpectedSqlSchema
} else {
sqlSchema = mustSqlSchema(test.ExpectedSchema)
}
assertSchemasEqual(t, sqlSchema, sch)
}
// TODO: this shouldn't be here
func createWorkingRootUpdate() map[string]TableUpdate {
return map[string]TableUpdate{
@@ -1618,114 +1547,6 @@ func createWorkingRootUpdate() map[string]TableUpdate {
}
}
func updateTables(t *testing.T, ctx context.Context, root doltdb.RootValue, tblUpdates map[string]TableUpdate) doltdb.RootValue {
for tblName, updates := range tblUpdates {
tbl, ok, err := root.GetTable(ctx, doltdb.TableName{Name: tblName})
require.NoError(t, err)
var sch schema.Schema
if updates.NewSch != nil {
sch = updates.NewSch
} else {
sch, err = tbl.GetSchema(ctx)
require.NoError(t, err)
}
var rowData types.Map
if updates.NewRowData == nil {
if ok {
rowData, err = tbl.GetNomsRowData(ctx)
require.NoError(t, err)
} else {
rowData, err = types.NewMap(ctx, root.VRW())
require.NoError(t, err)
}
} else {
rowData = *updates.NewRowData
}
if updates.RowUpdates != nil {
me := rowData.Edit()
for _, r := range updates.RowUpdates {
me = me.Set(r.NomsMapKey(sch), r.NomsMapValue(sch))
}
rowData, err = me.Map(ctx)
require.NoError(t, err)
}
var indexData durable.IndexSet
require.NoError(t, err)
if tbl != nil {
indexData, err = tbl.GetIndexSet(ctx)
require.NoError(t, err)
}
tbl, err = doltdb.NewNomsTable(ctx, root.VRW(), root.NodeStore(), sch, rowData, indexData, nil)
require.NoError(t, err)
root, err = root.PutTable(ctx, doltdb.TableName{Name: tblName}, tbl)
require.NoError(t, err)
}
return root
}
// initializeWithHistory will go through the provided historyNodes and create the intended commit graph
func initializeWithHistory(t *testing.T, ctx context.Context, dEnv *env.DoltEnv, historyNodes ...HistoryNode) {
for _, node := range historyNodes {
cs, err := doltdb.NewCommitSpec(env.DefaultInitBranch)
require.NoError(t, err)
optCmt, err := dEnv.DoltDB(ctx).Resolve(ctx, cs, nil)
require.NoError(t, err)
cm, ok := optCmt.ToCommit()
require.True(t, ok)
processNode(t, ctx, dEnv, node, cm)
}
}
func processNode(t *testing.T, ctx context.Context, dEnv *env.DoltEnv, node HistoryNode, parent *doltdb.Commit) {
branchRef := ref.NewBranchRef(node.Branch)
ok, err := dEnv.DoltDB(ctx).HasRef(ctx, branchRef)
require.NoError(t, err)
if !ok {
err = dEnv.DoltDB(ctx).NewBranchAtCommit(ctx, branchRef, parent, nil)
require.NoError(t, err)
}
cs, err := doltdb.NewCommitSpec(branchRef.String())
require.NoError(t, err)
optCmt, err := dEnv.DoltDB(ctx).Resolve(ctx, cs, nil)
require.NoError(t, err)
cm, ok := optCmt.ToCommit()
require.True(t, ok)
root, err := cm.GetRootValue(ctx)
require.NoError(t, err)
root = updateTables(t, ctx, root, node.Updates)
r, h, err := dEnv.DoltDB(ctx).WriteRootValue(ctx, root)
require.NoError(t, err)
root = r
meta, err := datas.NewCommitMeta("Ash Ketchum", "ash@poke.mon", node.CommitMsg)
require.NoError(t, err)
cm, err = dEnv.DoltDB(ctx).Commit(ctx, h, branchRef, meta)
require.NoError(t, err)
for _, child := range node.Children {
processNode(t, ctx, dEnv, child, cm)
}
}
func validateTest(t *testing.T, test SelectTest) {
if (test.ExpectedRows == nil) != (test.ExpectedSchema == nil && test.ExpectedSqlSchema == nil) {
require.Fail(t, "Incorrect test setup: schema and rows must both be provided if one is")
@@ -890,3 +890,33 @@ ALTER TABLE three ADD FOREIGN KEY (v1, v2) REFERENCES two(v1, v2) ON DELETE CASC
})
}
}
func convertSqlRowToInt64(sqlRows []sql.Row) []sql.Row {
if sqlRows == nil {
return nil
}
newSqlRows := make([]sql.Row, len(sqlRows))
for i, sqlRow := range sqlRows {
newSqlRow := make(sql.Row, len(sqlRow))
for j := range sqlRow {
switch v := sqlRow[j].(type) {
case int:
newSqlRow[j] = int64(v)
case int8:
newSqlRow[j] = int64(v)
case int16:
newSqlRow[j] = int64(v)
case int32:
newSqlRow[j] = int64(v)
case int64:
newSqlRow[j] = v
case nil:
newSqlRow[j] = nil
default:
return sqlRows
}
}
newSqlRows[i] = newSqlRow
}
return newSqlRows
}
@@ -1,386 +0,0 @@
// Copyright 2020 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 sqle
import (
"context"
"strings"
"testing"
"github.com/dolthub/go-mysql-server/sql"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
"github.com/dolthub/dolt/go/store/types"
)
func setupEditorIndexTest(ctx context.Context, t *testing.T) (*env.DoltEnv, doltdb.RootValue) {
index_dEnv := dtestutils.CreateTestEnv()
root, err := index_dEnv.WorkingRoot(context.Background())
require.NoError(t, err)
index_initialRoot, err := ExecuteSql(ctx, index_dEnv, root, `
CREATE TABLE onepk (
pk1 BIGINT PRIMARY KEY,
v1 BIGINT,
v2 BIGINT
);
CREATE TABLE twopk (
pk1 BIGINT,
pk2 BIGINT,
v1 BIGINT,
v2 BIGINT,
PRIMARY KEY (pk1,pk2)
);
CREATE TABLE oneuni (
pk1 BIGINT PRIMARY KEY,
v1 BIGINT,
v2 BIGINT
);
CREATE TABLE twouni (
pk1 BIGINT,
pk2 BIGINT,
v1 BIGINT,
v2 BIGINT,
PRIMARY KEY (pk1,pk2)
);
CREATE INDEX idx_v1 ON onepk(v1);
CREATE INDEX idx_v2v1 ON twopk(v2, v1);
CREATE UNIQUE INDEX idx_v1 ON oneuni(v1);
CREATE UNIQUE INDEX idx_v1v2 ON twouni(v1, v2);
`)
require.NoError(t, err)
return index_dEnv, index_initialRoot
}
func TestTableEditorIndexResults(t *testing.T) {
tests := []struct {
sqlStatement string
expectedIdxv1 []sql.Row
expectedIdxv2v1 []sql.Row
}{
{
`
INSERT INTO onepk VALUES (1, 2, 3), (4, 5, 6), (7, 8, 9);
INSERT INTO onepk VALUES (3, 2, 1), (6, 5, 4), (9, 8, 7);
`,
[]sql.Row{{2, 1}, {2, 3}, {5, 4}, {5, 6}, {8, 7}, {8, 9}},
[]sql.Row{},
},
{
`
INSERT INTO onepk VALUES (1, 11, 111), (2, 22, 222), (3, 33, 333);
UPDATE onepk SET v1 = v1 - 1 WHERE pk1 > 1;
REPLACE INTO onepk VALUES (3, 55, 555), (4, 44, 444);
`,
[]sql.Row{{11, 1}, {21, 2}, {44, 4}, {55, 3}},
[]sql.Row{},
},
{
`
INSERT INTO onepk VALUES (1, 11, 111), (2, 22, 222), (3, 33, 333);
INSERT INTO twopk VALUES (4, 44, 444, 4444);
REPLACE INTO twopk VALUES (4, 44, 111, 4444), (5, 55, 222, 5555), (6, 66, 333, 6666);
DELETE FROM onepk WHERE v2 = 222;
`,
[]sql.Row{{11, 1}, {33, 3}},
[]sql.Row{{4444, 111, 4, 44}, {5555, 222, 5, 55}, {6666, 333, 6, 66}},
},
{
`
INSERT INTO onepk VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);
DELETE FROM onepk WHERE pk1 % 2 = 1;
REPLACE INTO onepk VALUES (3, 6, 2), (-1, 4, -3);
UPDATE onepk SET pk1 = v1 + pk1 ORDER BY pk1 DESC;
`,
[]sql.Row{{2, 4}, {4, 3}, {6, 9}},
[]sql.Row{},
},
}
for _, test := range tests {
t.Run(test.sqlStatement, func(t *testing.T) {
ctx := context.Background()
dEnv, initialRoot := setupEditorIndexTest(ctx, t)
defer dEnv.DoltDB(ctx).Close()
root := initialRoot
for _, sqlStatement := range strings.Split(test.sqlStatement, ";") {
var err error
root, err = executeModify(t, context.Background(), dEnv, root, sqlStatement)
require.NoError(t, err)
}
onepk, ok, err := root.GetTable(context.Background(), doltdb.TableName{Name: "onepk"})
require.NoError(t, err)
require.True(t, ok)
twopk, ok, err := root.GetTable(context.Background(), doltdb.TableName{Name: "twopk"})
require.NoError(t, err)
require.True(t, ok)
onepkSch, err := onepk.GetSchema(context.Background())
require.NoError(t, err)
twopkSch, err := twopk.GetSchema(context.Background())
require.NoError(t, err)
idx_v1 := onepkSch.Indexes().GetByName("idx_v1")
require.NotNil(t, idx_v1)
idx_v2v1 := twopkSch.Indexes().GetByName("idx_v2v1")
require.NotNil(t, idx_v2v1)
if types.Format_Default != types.Format_LD_1 {
t.Skip("need a prolly sql row iter")
}
idx_v1RowData, err := onepk.GetNomsIndexRowData(context.Background(), idx_v1.Name())
require.NoError(t, err)
idx_v2v1RowData, err := twopk.GetNomsIndexRowData(context.Background(), idx_v2v1.Name())
require.NoError(t, err)
if assert.Equal(t, uint64(len(test.expectedIdxv1)), idx_v1RowData.Len()) && len(test.expectedIdxv1) > 0 {
var sqlRows []sql.Row
_ = idx_v1RowData.IterAll(context.Background(), func(key, value types.Value) error {
r, err := row.FromNoms(idx_v1.Schema(), key.(types.Tuple), value.(types.Tuple))
assert.NoError(t, err)
sqlRow, err := sqlutil.DoltRowToSqlRow(r, idx_v1.Schema())
assert.NoError(t, err)
sqlRows = append(sqlRows, sqlRow)
return nil
})
assert.ElementsMatch(t, convertSqlRowToInt64(test.expectedIdxv1), sqlRows)
}
if assert.Equal(t, uint64(len(test.expectedIdxv2v1)), idx_v2v1RowData.Len()) && len(test.expectedIdxv2v1) > 0 {
var sqlRows []sql.Row
_ = idx_v2v1RowData.IterAll(context.Background(), func(key, value types.Value) error {
r, err := row.FromNoms(idx_v2v1.Schema(), key.(types.Tuple), value.(types.Tuple))
assert.NoError(t, err)
sqlRow, err := sqlutil.DoltRowToSqlRow(r, idx_v2v1.Schema())
assert.NoError(t, err)
sqlRows = append(sqlRows, sqlRow)
return nil
})
assert.ElementsMatch(t, convertSqlRowToInt64(test.expectedIdxv2v1), sqlRows)
}
})
}
}
func TestTableEditorUniqueIndexResults(t *testing.T) {
tests := []struct {
sqlStatement string
expectedIdxv1 []sql.Row
expectedIdxv1v2 []sql.Row
expectedErr bool
}{
{
`
INSERT INTO oneuni VALUES (1, 3, 2), (4, 6, 5), (7, 9, 8);
INSERT INTO oneuni VALUES (3, 1, 2), (6, 4, 5), (9, 7, 8);
`,
[]sql.Row{{1, 3}, {3, 1}, {4, 6}, {6, 4}, {7, 9}, {9, 7}},
[]sql.Row{},
false,
},
{
`
INSERT INTO oneuni VALUES (1, 11, 111), (2, 22, 222), (3, 33, 333);
UPDATE oneuni SET v1 = v1 - 1 WHERE pk1 > 1;
REPLACE INTO oneuni VALUES (3, 55, 555), (4, 44, 444);
`,
[]sql.Row{{11, 1}, {21, 2}, {44, 4}, {55, 3}},
[]sql.Row{},
false,
},
{
`
INSERT INTO oneuni VALUES (1, 11, 111), (2, 22, 222), (3, 33, 333);
REPLACE INTO oneuni VALUES (1, 11, 444);
INSERT INTO twouni VALUES (4, 44, 444, 4444);
REPLACE INTO twouni VALUES (4, 44, 111, 4444), (5, 55, 222, 5555), (6, 66, 333, 6666);
DELETE FROM oneuni WHERE v1 = 22;
`,
[]sql.Row{{11, 1}, {33, 3}},
[]sql.Row{{111, 4444, 4, 44}, {222, 5555, 5, 55}, {333, 6666, 6, 66}},
false,
},
{
`
INSERT INTO oneuni VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);
DELETE FROM oneuni WHERE pk1 % 2 = 1;
REPLACE INTO oneuni VALUES (3, 6, 2), (-1, 4, -3);
UPDATE oneuni SET pk1 = v1 + v2;
`,
[]sql.Row{{2, 4}, {4, 1}, {6, 8}},
[]sql.Row{},
false,
},
{
`
INSERT INTO oneuni VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);
DELETE FROM oneuni WHERE v1 < 3;
REPLACE INTO oneuni VALUES (4, 2, 2), (5, 3, 3), (3, 1, 1);
`,
[]sql.Row{{1, 3}, {2, 4}, {3, 5}},
[]sql.Row{},
false,
},
{
`
INSERT INTO oneuni VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);
DELETE FROM oneuni WHERE v1 < 3;
REPLACE INTO oneuni VALUES (4, 2, 2), (5, 2, 3), (3, 1, 1);
`,
[]sql.Row{{1, 3}, {2, 5}},
[]sql.Row{},
false,
},
{
`
INSERT INTO oneuni VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);
REPLACE INTO oneuni VALUES (1, 1, 1), (2, 2, 2), (3, 2, 3);
`,
[]sql.Row{{1, 1}, {2, 3}},
[]sql.Row{},
false,
},
{
`
INSERT INTO oneuni VALUES (1, 1, 1), (2, 1, 2), (3, 3, 3);
`,
[]sql.Row{},
[]sql.Row{},
true,
},
{
`
INSERT INTO oneuni VALUES (1, 2, 3), (2, 1, 4);
UPDATE oneuni SET v1 = v1 + pk1;
`,
[]sql.Row{},
[]sql.Row{},
true,
},
}
for _, test := range tests {
t.Run(test.sqlStatement, func(t *testing.T) {
ctx := context.Background()
dEnv, initialRoot := setupEditorIndexTest(ctx, t)
defer dEnv.DoltDB(ctx).Close()
root := initialRoot
var err error
for _, sqlStatement := range strings.Split(test.sqlStatement, ";") {
root, err = executeModify(t, context.Background(), dEnv, root, sqlStatement)
if err != nil {
break
}
}
if test.expectedErr {
require.Error(t, err)
return
}
require.NoError(t, err)
oneuni, ok, err := root.GetTable(context.Background(), doltdb.TableName{Name: "oneuni"})
require.NoError(t, err)
require.True(t, ok)
twouni, ok, err := root.GetTable(context.Background(), doltdb.TableName{Name: "twouni"})
require.NoError(t, err)
require.True(t, ok)
oneuniSch, err := oneuni.GetSchema(context.Background())
require.NoError(t, err)
twouniSch, err := twouni.GetSchema(context.Background())
require.NoError(t, err)
idx_v1 := oneuniSch.Indexes().GetByName("idx_v1")
require.NotNil(t, idx_v1)
idx_v1v2 := twouniSch.Indexes().GetByName("idx_v1v2")
require.NotNil(t, idx_v1v2)
if types.Format_Default != types.Format_LD_1 {
t.Skip("need a prolly sql row iter")
}
idx_v1RowData, err := oneuni.GetNomsIndexRowData(context.Background(), idx_v1.Name())
require.NoError(t, err)
idx_v1v2RowData, err := twouni.GetNomsIndexRowData(context.Background(), idx_v1v2.Name())
require.NoError(t, err)
if assert.Equal(t, uint64(len(test.expectedIdxv1)), idx_v1RowData.Len()) && len(test.expectedIdxv1) > 0 {
var sqlRows []sql.Row
_ = idx_v1RowData.IterAll(context.Background(), func(key, value types.Value) error {
r, err := row.FromNoms(idx_v1.Schema(), key.(types.Tuple), value.(types.Tuple))
assert.NoError(t, err)
sqlRow, err := sqlutil.DoltRowToSqlRow(r, idx_v1.Schema())
assert.NoError(t, err)
sqlRows = append(sqlRows, sqlRow)
return nil
})
assert.ElementsMatch(t, convertSqlRowToInt64(test.expectedIdxv1), sqlRows)
}
if assert.Equal(t, uint64(len(test.expectedIdxv1v2)), idx_v1v2RowData.Len()) && len(test.expectedIdxv1v2) > 0 {
var sqlRows []sql.Row
_ = idx_v1v2RowData.IterAll(context.Background(), func(key, value types.Value) error {
r, err := row.FromNoms(idx_v1v2.Schema(), key.(types.Tuple), value.(types.Tuple))
assert.NoError(t, err)
sqlRow, err := sqlutil.DoltRowToSqlRow(r, idx_v1v2.Schema())
assert.NoError(t, err)
sqlRows = append(sqlRows, sqlRow)
return nil
})
assert.ElementsMatch(t, convertSqlRowToInt64(test.expectedIdxv1v2), sqlRows)
}
})
}
}
func convertSqlRowToInt64(sqlRows []sql.Row) []sql.Row {
if sqlRows == nil {
return nil
}
newSqlRows := make([]sql.Row, len(sqlRows))
for i, sqlRow := range sqlRows {
newSqlRow := make(sql.Row, len(sqlRow))
for j := range sqlRow {
switch v := sqlRow[j].(type) {
case int:
newSqlRow[j] = int64(v)
case int8:
newSqlRow[j] = int64(v)
case int16:
newSqlRow[j] = int64(v)
case int32:
newSqlRow[j] = int64(v)
case int64:
newSqlRow[j] = v
case nil:
newSqlRow[j] = nil
default:
return sqlRows
}
}
newSqlRows[i] = newSqlRow
}
return newSqlRows
}
+8 -105
View File
@@ -1090,40 +1090,12 @@ func (t *WritableDoltTable) truncate(
}
func copyConstraintViolationsAndConflicts(ctx context.Context, from, to *doltdb.Table) (*doltdb.Table, error) {
if !types.IsFormat_DOLT(to.Format()) {
if has, err := from.HasConflicts(ctx); err != nil {
return nil, err
} else if has {
confSch, conf, err := from.GetConflicts(ctx)
if err != nil {
return nil, err
}
to, err = to.SetConflicts(ctx, confSch, conf)
if err != nil {
return nil, err
}
}
viols, err := from.GetConstraintViolations(ctx)
if err != nil {
return nil, err
}
to, err = to.SetConstraintViolations(ctx, viols)
if err != nil {
return nil, err
}
} else {
arts, err := from.GetArtifacts(ctx)
if err != nil {
return nil, err
}
to, err = to.SetArtifacts(ctx, arts)
if err != nil {
return nil, err
}
types.AssertFormat_DOLT(to.Format())
arts, err := from.GetArtifacts(ctx)
if err != nil {
return nil, err
}
return to, nil
return to.SetArtifacts(ctx, arts)
}
// Updater implements sql.UpdatableTable
@@ -1264,7 +1236,7 @@ func (t *DoltTable) GetDeclaredForeignKeys(ctx *sql.Context) ([]sql.ForeignKeyCo
for i, fk := range declaredFks {
if len(fk.UnresolvedFKDetails.TableColumns) > 0 && len(fk.UnresolvedFKDetails.ReferencedTableColumns) > 0 {
//TODO: implement multi-db support for foreign keys
// TODO: implement multi-db support for foreign keys
toReturn[i] = sql.ForeignKeyConstraint{
Name: fk.Name,
Database: t.db.Name(),
@@ -1496,14 +1468,6 @@ func (p doltTablePartition) Key() []byte {
return []byte(strconv.FormatUint(p.start, 10) + " >= i < " + strconv.FormatUint(p.end, 10))
}
// IteratorForPartition returns a types.MapIterator implementation which will iterate through the values
// for index = start; index < end. This iterator is not thread safe and should only be used from a single go routine
// unless paired with a mutex
func (p doltTablePartition) IteratorForPartition(ctx context.Context, idx durable.Index) (types.MapTupleIterator, error) {
m := durable.NomsMapFromIndex(idx)
return m.RangeIterator(ctx, p.start, p.end)
}
// AlterableDoltTable allows altering the schema of the table. It implements sql.AlterableTable.
type AlterableDoltTable struct {
WritableDoltTable
@@ -2189,66 +2153,6 @@ func (t *AlterableDoltTable) DropColumn(*sql.Context, string) error {
return fmt.Errorf("not implemented: AlterableDoltTable.DropColumn()")
}
// dropColumnData drops values for the specified column from the underlying storage layer
func (t *AlterableDoltTable) dropColumnData(ctx *sql.Context, updatedTable *doltdb.Table, sch schema.Schema, columnName string) (*doltdb.Table, error) {
nomsRowData, err := updatedTable.GetNomsRowData(ctx)
if err != nil {
return nil, err
}
column, ok := sch.GetAllCols().GetByName(columnName)
if !ok {
return nil, sql.ErrColumnNotFound.New(columnName)
}
mapEditor := nomsRowData.Edit()
defer mapEditor.Close(ctx)
err = nomsRowData.Iter(ctx, func(key, value types.Value) (stop bool, err error) {
if t, ok := value.(types.Tuple); ok {
newTuple, err := types.NewTuple(nomsRowData.Format())
if err != nil {
return true, err
}
idx := uint64(0)
for idx < t.Len() {
tTag, err := t.Get(idx)
if err != nil {
return true, err
}
tValue, err := t.Get(idx + 1)
if err != nil {
return true, err
}
if tTag.Equals(types.Uint(column.Tag)) == false {
newTuple, err = newTuple.Append(tTag, tValue)
if err != nil {
return true, err
}
}
idx += 2
}
mapEditor.Set(key, newTuple)
}
return false, nil
})
if err != nil {
return nil, err
}
newMapData, err := mapEditor.Map(ctx)
if err != nil {
return nil, err
}
return updatedTable.UpdateNomsRows(ctx, newMapData)
}
// ModifyColumn implements sql.AlterableTable. ModifyColumn operations are only used for operations that change only
// the schema of a table, not the data. For those operations, |RewriteInserter| is used.
func (t *AlterableDoltTable) ModifyColumn(ctx *sql.Context, columnName string, column *sql.Column, order *sql.ColumnOrder) error {
@@ -2512,9 +2416,8 @@ func (t *AlterableDoltTable) RenameIndex(ctx *sql.Context, fromIndexName string,
// CreateFulltextIndex implements fulltext.IndexAlterableTable
func (t *AlterableDoltTable) CreateFulltextIndex(ctx *sql.Context, idx sql.IndexDef, keyCols fulltext.KeyColumns, tableNames fulltext.IndexTableNames) error {
if !types.IsFormat_DOLT(t.Format()) {
return fmt.Errorf("FULLTEXT is not supported on storage format %s. Run `dolt migrate` to upgrade to the latest storage format.", t.Format().VersionString())
}
types.AssertFormat_DOLT(t.Format())
if err := dsess.CheckAccessForDb(ctx, t.db, branch_control.Permissions_Write); err != nil {
return err
}
+1 -14
View File
@@ -551,20 +551,7 @@ func SqlRowsFromDurableIndex(idx durable.Index, sch schema.Schema) ([]sql.Row, e
}
} else {
// types.Format_LD_1
rowData := durable.NomsMapFromIndex(idx)
_ = rowData.IterAll(ctx, func(key, value types.Value) error {
r, err := row.FromNoms(sch, key.(types.Tuple), value.(types.Tuple))
if err != nil {
return err
}
sqlRow, err := sqlutil.DoltRowToSqlRow(r, sch)
if err != nil {
return err
}
sqlRows = append(sqlRows, sqlRow)
return nil
})
panic("Unsupported format: " + idx.Format().VersionString())
}
return sqlRows, nil
}
@@ -19,38 +19,20 @@ import (
"sync"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/row"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/types"
)
const (
// The number of rows we expect the test to end up with
indexEditorConcurrencyFinalCount = 100
idTag = 0
firstTag = 1
lastTag = 2
isMarriedTag = 3
ageTag = 4
emptyTag = 5
testSchemaIndexName = "idx_name"
testSchemaIndexAge = "idx_age"
)
var id0, _ = uuid.NewRandom()
var id1, _ = uuid.NewRandom()
var id2, _ = uuid.NewRandom()
var id3, _ = uuid.NewRandom()
func TestIndexEditorConcurrency(t *testing.T) {
format := types.Format_LD_1
_, vrw, _, err := dbfactory.MemFactory{}.CreateDB(context.Background(), format, nil, nil)
@@ -451,100 +433,3 @@ func TestIndexEditorCapacityExceeded(t *testing.T) {
_, err = indexEditor.Map(ctx)
require.Contains(t, err.Error(), "unrecoverable state")
}
func createTestRowData(t *testing.T, vrw types.ValueReadWriter, sch schema.Schema) (types.Map, []row.Row) {
return createTestRowDataFromTaggedValues(t, vrw, sch,
row.TaggedValues{
idTag: types.InlineBlob(id0[:]), firstTag: types.String("bill"), lastTag: types.String("billerson"), ageTag: types.Uint(53)},
row.TaggedValues{
idTag: types.InlineBlob(id1[:]), firstTag: types.String("eric"), lastTag: types.String("ericson"), isMarriedTag: types.Int(1), ageTag: types.Uint(21)},
row.TaggedValues{
idTag: types.InlineBlob(id2[:]), firstTag: types.String("john"), lastTag: types.String("johnson"), isMarriedTag: types.Int(0), ageTag: types.Uint(53)},
row.TaggedValues{
idTag: types.InlineBlob(id3[:]), firstTag: types.String("robert"), lastTag: types.String("robertson"), ageTag: types.Uint(36)},
)
}
func createUpdatedTestRowData(t *testing.T, vrw types.ValueReadWriter, sch schema.Schema) (types.Map, []row.Row) {
return createTestRowDataFromTaggedValues(t, vrw, sch,
row.TaggedValues{
idTag: types.InlineBlob(id0[:]), firstTag: types.String("jack"), lastTag: types.String("space"), ageTag: types.Uint(20)},
row.TaggedValues{
idTag: types.InlineBlob(id1[:]), firstTag: types.String("rick"), lastTag: types.String("drive"), isMarriedTag: types.Int(0), ageTag: types.Uint(21)},
row.TaggedValues{
idTag: types.InlineBlob(id2[:]), firstTag: types.String("tyler"), lastTag: types.String("eat"), isMarriedTag: types.Int(1), ageTag: types.Uint(22)},
row.TaggedValues{
idTag: types.InlineBlob(id3[:]), firstTag: types.String("moore"), lastTag: types.String("walk"), ageTag: types.Uint(23)},
)
}
func createTestRowDataFromTaggedValues(t *testing.T, vrw types.ValueReadWriter, sch schema.Schema, vals ...row.TaggedValues) (types.Map, []row.Row) {
var err error
rows := make([]row.Row, len(vals))
m, err := types.NewMap(context.Background(), vrw)
assert.NoError(t, err)
ed := m.Edit()
for i, val := range vals {
r, err := row.New(types.Format_LD_1, sch, val)
require.NoError(t, err)
rows[i] = r
ed = ed.Set(r.NomsMapKey(sch), r.NomsMapValue(sch))
}
m, err = ed.Map(context.Background())
assert.NoError(t, err)
return m, rows
}
func createTestSchema(t *testing.T) schema.Schema {
colColl := schema.NewColCollection(
schema.NewColumn("id", idTag, types.InlineBlobKind, true, schema.NotNullConstraint{}),
schema.NewColumn("first", firstTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("last", lastTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("is_married", isMarriedTag, types.IntKind, false),
schema.NewColumn("age", ageTag, types.UintKind, false),
schema.NewColumn("empty", emptyTag, types.IntKind, false),
)
sch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexName, []uint64{firstTag, lastTag}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
require.NoError(t, err)
_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexAge, []uint64{ageTag}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
require.NoError(t, err)
return sch
}
func createTableWithoutIndexRebuilding(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, sch schema.Schema, rowData types.Map) (*doltdb.Table, error) {
return doltdb.NewNomsTable(ctx, vrw, ns, sch, rowData, nil, nil)
}
func rowsToIndexRows(t *testing.T, rows []row.Row, indexName schema.Index, indexAge schema.Index) (indexNameExpectedRows []row.Row, indexAgeExpectedRows []row.Row) {
indexNameExpectedRows = make([]row.Row, len(rows))
indexAgeExpectedRows = make([]row.Row, len(rows))
indexNameSch := indexName.Schema()
indexAgeSch := indexAge.Schema()
var err error
for i, r := range rows {
indexNameKey := make(row.TaggedValues)
for _, tag := range indexName.AllTags() {
val, ok := r.GetColVal(tag)
require.True(t, ok)
indexNameKey[tag] = val
}
indexNameExpectedRows[i], err = row.New(types.Format_LD_1, indexNameSch, indexNameKey)
require.NoError(t, err)
indexAgeKey := make(row.TaggedValues)
for _, tag := range indexAge.AllTags() {
val, ok := r.GetColVal(tag)
require.True(t, ok)
indexAgeKey[tag] = val
}
indexAgeExpectedRows[i], err = row.New(types.Format_LD_1, indexAgeSch, indexAgeKey)
require.NoError(t, err)
}
return
}
+2 -25
View File
@@ -60,36 +60,13 @@ func (i rowIterImpl) Close(ctx context.Context) error {
// NewTableIterator creates a RowIter that iterates sql.Row's from |idx|.
// |offset| can be supplied to read at some start point in |idx|.
func NewTableIterator(ctx context.Context, sch schema.Schema, idx durable.Index) (RowIter, error) {
var rowItr sql.RowIter
if types.IsFormat_DOLT(idx.Format()) {
m := durable.MapFromIndex(idx)
itr, err := m.IterAll(ctx)
if err != nil {
return nil, err
}
rowItr = index.NewProllyRowIterForMap(sch, m, itr, nil)
if err != nil {
return nil, err
}
} else {
noms := durable.NomsMapFromIndex(idx)
itr, err := noms.IteratorAt(ctx, 0)
if err != nil {
return nil, err
}
conv := makeNomsConverter(idx.Format(), sch)
rowItr = index.NewDoltMapIter(itr.NextTuple, nil, conv)
return NewRowIter(index.NewProllyRowIterForMap(sch, m, itr, nil)), nil
}
return NewRowIter(rowItr), nil
}
// makeNomsConverter creates a *index.KVToSqlRowConverter.
func makeNomsConverter(nbf *types.NomsBinFormat, sch schema.Schema) *index.KVToSqlRowConverter {
cols := sch.GetAllCols().GetColumns()
tagToSqlColIdx := make(map[uint64]int)
for i, col := range cols {
tagToSqlColIdx[col.Tag] = i
}
return index.NewKVToSqlRowConverter(nbf, tagToSqlColIdx, cols, len(cols))
panic("Unsupported format: " + idx.Format().VersionString())
}
+6 -2
View File
@@ -16,6 +16,7 @@ package types
import (
"errors"
"fmt"
"sync"
"github.com/dolthub/dolt/go/store/constants"
@@ -58,8 +59,11 @@ func IsFormat_DOLT(nbf *NomsBinFormat) bool {
return nbf.tag == formatTag_DOLT
}
func IsFormat_LD(nbf *NomsBinFormat) bool {
return nbf.tag == formatTag_LD_1
// AssertFormat_DOLT panics if the provided NomsBinFormat is not compatible with the DOLT format.
func AssertFormat_DOLT(nbf *NomsBinFormat) {
if !IsFormat_DOLT(nbf) {
panic(fmt.Sprintf("Unsupported binary format %s, please migrate database to newer format", nbf.VersionString()))
}
}
func GetFormatForVersionString(s string) (*NomsBinFormat, error) {
-7
View File
@@ -54,7 +54,6 @@ teardown() {
[[ "$output" =~ "tag - Create, list, delete tags." ]] || false
[[ "$output" =~ "blame - Show what revision and author last modified each row of a table." ]] || false
[[ "$output" =~ "constraints - Commands for handling constraints." ]] || false
[[ "$output" =~ "migrate - Executes a database migration to use the latest Dolt data format." ]] || false
[[ "$output" =~ "read-tables - Fetch table(s) at a specific commit into a new dolt repo" ]] || false
[[ "$output" =~ "gc - Cleans up unreferenced data from the repository." ]] || false
[[ "$output" =~ "filter-branch - Edits the commit history using the provided query." ]] || false
@@ -343,12 +342,6 @@ NOT_VALID_REPO_ERROR="The current directory is not a valid dolt repository."
[[ "$output" =~ "resolve -" ]] || false
}
@test "no-repo: dolt migrate outside of a dolt repository" {
run dolt migrate
[ "$status" -ne 0 ]
[ "${lines[0]}" = "$NOT_VALID_REPO_ERROR" ]
}
@test "no-repo: initializing a dolt repository" {
mkdir dolt-repo-$$-new
cd dolt-repo-$$-new