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

     1  // Copyright 2020 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package dtables
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"io"
    21  	"strings"
    22  	"sync"
    23  
    24  	"github.com/dolthub/go-mysql-server/sql"
    25  
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/rowconv"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    31  	"github.com/dolthub/dolt/go/store/types"
    32  )
    33  
    34  const commitDiffDefaultRowCount = 1000
    35  
    36  var ErrExactlyOneToCommit = errors.New("dolt_commit_diff_* tables must be filtered to a single 'to_commit'")
    37  var ErrExactlyOneFromCommit = errors.New("dolt_commit_diff_* tables must be filtered to a single 'from_commit'")
    38  var ErrInvalidCommitDiffTableArgs = errors.New("commit_diff_<table> requires one 'to_commit' and one 'from_commit'")
    39  
    40  type CommitDiffTable struct {
    41  	name        string
    42  	dbName      string
    43  	ddb         *doltdb.DoltDB
    44  	joiner      *rowconv.Joiner
    45  	sqlSch      sql.PrimaryKeySchema
    46  	workingRoot doltdb.RootValue
    47  	// toCommit and fromCommit are set via the
    48  	// sql.IndexAddressable interface
    49  	toCommit          string
    50  	fromCommit        string
    51  	requiredFilterErr error
    52  	targetSchema      schema.Schema
    53  }
    54  
    55  var _ sql.Table = (*CommitDiffTable)(nil)
    56  var _ sql.IndexAddressable = (*CommitDiffTable)(nil)
    57  var _ sql.StatisticsTable = (*CommitDiffTable)(nil)
    58  
    59  func NewCommitDiffTable(ctx *sql.Context, dbName, tblName string, ddb *doltdb.DoltDB, root doltdb.RootValue) (sql.Table, error) {
    60  	diffTblName := doltdb.DoltCommitDiffTablePrefix + tblName
    61  
    62  	table, _, ok, err := doltdb.GetTableInsensitive(ctx, root, tblName)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	if !ok {
    67  		return nil, sql.ErrTableNotFound.New(diffTblName)
    68  	}
    69  
    70  	sch, err := table.GetSchema(ctx)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	diffTableSchema, j, err := GetDiffTableSchemaAndJoiner(ddb.Format(), sch, sch)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	sqlSch, err := sqlutil.FromDoltSchema(dbName, diffTblName, diffTableSchema)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	return &CommitDiffTable{
    86  		dbName:       dbName,
    87  		name:         tblName,
    88  		ddb:          ddb,
    89  		workingRoot:  root,
    90  		joiner:       j,
    91  		sqlSch:       sqlSch,
    92  		targetSchema: sch,
    93  	}, nil
    94  }
    95  
    96  func (dt *CommitDiffTable) DataLength(ctx *sql.Context) (uint64, error) {
    97  	numBytesPerRow := schema.SchemaAvgLength(dt.Schema())
    98  	numRows, _, err := dt.RowCount(ctx)
    99  	if err != nil {
   100  		return 0, err
   101  	}
   102  	return numBytesPerRow * numRows, nil
   103  }
   104  
   105  func (dt *CommitDiffTable) RowCount(_ *sql.Context) (uint64, bool, error) {
   106  	return commitDiffDefaultRowCount, false, nil
   107  }
   108  
   109  func (dt *CommitDiffTable) Name() string {
   110  	return doltdb.DoltCommitDiffTablePrefix + dt.name
   111  }
   112  
   113  func (dt *CommitDiffTable) String() string {
   114  	return doltdb.DoltCommitDiffTablePrefix + dt.name
   115  }
   116  
   117  func (dt *CommitDiffTable) Schema() sql.Schema {
   118  	return dt.sqlSch.Schema
   119  }
   120  
   121  // Collation implements the sql.Table interface.
   122  func (dt *CommitDiffTable) Collation() sql.CollationID {
   123  	return sql.Collation_Default
   124  }
   125  
   126  // GetIndexes implements sql.IndexAddressable
   127  func (dt *CommitDiffTable) GetIndexes(ctx *sql.Context) ([]sql.Index, error) {
   128  	return []sql.Index{index.DoltToFromCommitIndex(dt.name)}, nil
   129  }
   130  
   131  // IndexedAccess implements sql.IndexAddressable
   132  func (dt *CommitDiffTable) IndexedAccess(lookup sql.IndexLookup) sql.IndexedTable {
   133  	nt := *dt
   134  	return &nt
   135  }
   136  
   137  func (dt *CommitDiffTable) PreciseMatch() bool {
   138  	return false
   139  }
   140  
   141  // RequiredPredicates implements sql.IndexRequired
   142  func (dt *CommitDiffTable) RequiredPredicates() []string {
   143  	return []string{"to_commit", "from_commit"}
   144  }
   145  
   146  func (dt *CommitDiffTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
   147  	return nil, fmt.Errorf("error querying table %s: %w", dt.Name(), ErrExactlyOneToCommit)
   148  }
   149  
   150  func (dt *CommitDiffTable) LookupPartitions(ctx *sql.Context, i sql.IndexLookup) (sql.PartitionIter, error) {
   151  	if len(i.Ranges) != 1 || len(i.Ranges[0]) != 2 {
   152  		return nil, ErrInvalidCommitDiffTableArgs
   153  	}
   154  	to := i.Ranges[0][0]
   155  	from := i.Ranges[0][1]
   156  	switch to.UpperBound.(type) {
   157  	case sql.Above, sql.Below:
   158  	default:
   159  		return nil, ErrInvalidCommitDiffTableArgs
   160  	}
   161  	switch from.UpperBound.(type) {
   162  	case sql.Above, sql.Below:
   163  	default:
   164  		return nil, ErrInvalidCommitDiffTableArgs
   165  	}
   166  	toCommit, _, err := to.Typ.Convert(sql.GetRangeCutKey(to.UpperBound))
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  	var ok bool
   171  	dt.toCommit, ok = toCommit.(string)
   172  	if !ok {
   173  		return nil, fmt.Errorf("to_commit must be string, found %T", toCommit)
   174  	}
   175  	fromCommit, _, err := from.Typ.Convert(sql.GetRangeCutKey(from.UpperBound))
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	dt.fromCommit, ok = fromCommit.(string)
   180  	if !ok {
   181  		return nil, fmt.Errorf("from_commit must be string, found %T", fromCommit)
   182  	}
   183  
   184  	toRoot, toHash, toDate, err := dt.rootValForHash(ctx, dt.toCommit)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	fromRoot, fromHash, fromDate, err := dt.rootValForHash(ctx, dt.fromCommit)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	toTable, _, _, err := doltdb.GetTableInsensitive(ctx, toRoot, dt.name)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  
   199  	fromTable, _, _, err := doltdb.GetTableInsensitive(ctx, fromRoot, dt.name)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  
   204  	dp := DiffPartition{
   205  		to:       toTable,
   206  		from:     fromTable,
   207  		toName:   toHash,
   208  		fromName: fromHash,
   209  		toDate:   toDate,
   210  		fromDate: fromDate,
   211  		toSch:    dt.targetSchema,
   212  		fromSch:  dt.targetSchema,
   213  	}
   214  
   215  	isDiffable, err := dp.isDiffablePartition(ctx)
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	if !isDiffable {
   221  		ctx.Warn(PrimaryKeyChangeWarningCode, fmt.Sprintf(PrimaryKeyChangeWarning, dp.fromName, dp.toName))
   222  		return NewSliceOfPartitionsItr([]sql.Partition{}), nil
   223  	}
   224  
   225  	return NewSliceOfPartitionsItr([]sql.Partition{dp}), nil
   226  }
   227  
   228  type SliceOfPartitionsItr struct {
   229  	partitions []sql.Partition
   230  	i          int
   231  	mu         *sync.Mutex
   232  }
   233  
   234  func NewSliceOfPartitionsItr(partitions []sql.Partition) *SliceOfPartitionsItr {
   235  	return &SliceOfPartitionsItr{
   236  		partitions: partitions,
   237  		mu:         &sync.Mutex{},
   238  	}
   239  }
   240  
   241  func (itr *SliceOfPartitionsItr) Next(*sql.Context) (sql.Partition, error) {
   242  	itr.mu.Lock()
   243  	defer itr.mu.Unlock()
   244  
   245  	if itr.i >= len(itr.partitions) {
   246  		return nil, io.EOF
   247  	}
   248  
   249  	next := itr.partitions[itr.i]
   250  	itr.i++
   251  
   252  	return next, nil
   253  }
   254  
   255  func (itr *SliceOfPartitionsItr) Close(*sql.Context) error {
   256  	return nil
   257  }
   258  
   259  func (dt *CommitDiffTable) rootValForHash(ctx *sql.Context, hashStr string) (doltdb.RootValue, string, *types.Timestamp, error) {
   260  	var root doltdb.RootValue
   261  	var commitTime *types.Timestamp
   262  	if strings.ToLower(hashStr) == "working" {
   263  		root = dt.workingRoot
   264  	} else {
   265  		cs, err := doltdb.NewCommitSpec(hashStr)
   266  		if err != nil {
   267  			return nil, "", nil, err
   268  		}
   269  
   270  		optCmt, err := dt.ddb.Resolve(ctx, cs, nil)
   271  		if err != nil {
   272  			return nil, "", nil, err
   273  		}
   274  		cm, ok := optCmt.ToCommit()
   275  		if !ok {
   276  			return nil, "", nil, doltdb.ErrGhostCommitEncountered
   277  		}
   278  
   279  		root, err = cm.GetRootValue(ctx)
   280  
   281  		if err != nil {
   282  			return nil, "", nil, err
   283  		}
   284  
   285  		meta, err := cm.GetCommitMeta(ctx)
   286  
   287  		if err != nil {
   288  			return nil, "", nil, err
   289  		}
   290  
   291  		t := meta.Time()
   292  		commitTime = (*types.Timestamp)(&t)
   293  	}
   294  
   295  	return root, hashStr, commitTime, nil
   296  }
   297  
   298  func (dt *CommitDiffTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) {
   299  	dp := part.(DiffPartition)
   300  	return dp.GetRowIter(ctx, dt.ddb, dt.joiner, sql.IndexLookup{})
   301  }