github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/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  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"io"
    22  	"strings"
    23  	"sync"
    24  
    25  	"github.com/dolthub/go-mysql-server/sql"
    26  	"github.com/dolthub/go-mysql-server/sql/expression"
    27  	"github.com/dolthub/go-mysql-server/sql/parse"
    28  
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/diff"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    31  	"github.com/dolthub/dolt/go/libraries/doltcore/rowconv"
    32  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    33  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    34  	"github.com/dolthub/dolt/go/store/types"
    35  )
    36  
    37  var ErrExactlyOneToCommit = errors.New("dolt_commit_diff_* tables must be filtered to a single 'to_commit'")
    38  var ErrExactlyOneFromCommit = errors.New("dolt_commit_diff_* tables must be filtered to a single 'from_commit'")
    39  
    40  var _ sql.Table = (*CommitDiffTable)(nil)
    41  var _ sql.FilteredTable = (*CommitDiffTable)(nil)
    42  
    43  type CommitDiffTable struct {
    44  	name              string
    45  	ddb               *doltdb.DoltDB
    46  	ss                *schema.SuperSchema
    47  	joiner            *rowconv.Joiner
    48  	sqlSch            sql.Schema
    49  	workingRoot       *doltdb.RootValue
    50  	fromCommitFilter  *expression.Equals
    51  	toCommitFilter    *expression.Equals
    52  	requiredFilterErr error
    53  }
    54  
    55  func NewCommitDiffTable(ctx *sql.Context, tblName string, ddb *doltdb.DoltDB, root *doltdb.RootValue) (sql.Table, error) {
    56  	diffTblName := doltdb.DoltCommitDiffTablePrefix + tblName
    57  
    58  	ss, err := calcSuperDuperSchema(ctx, ddb, root, tblName)
    59  
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	_ = ss.AddColumn(schema.NewColumn("commit", schema.DiffCommitTag, types.StringKind, false))
    65  	_ = ss.AddColumn(schema.NewColumn("commit_date", schema.DiffCommitDateTag, types.TimestampKind, false))
    66  
    67  	sch, err := ss.GenerateSchema()
    68  
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	if sch.GetAllCols().Size() <= 1 {
    74  		return nil, sql.ErrTableNotFound.New(diffTblName)
    75  	}
    76  
    77  	j, err := rowconv.NewJoiner(
    78  		[]rowconv.NamedSchema{{Name: diff.To, Sch: sch}, {Name: diff.From, Sch: sch}},
    79  		map[string]rowconv.ColNamingFunc{
    80  			diff.To:   toNamer,
    81  			diff.From: fromNamer,
    82  		})
    83  
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	sqlSch, err := sqlutil.FromDoltSchema(diffTblName, j.GetSchema())
    89  
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	// parses to literal, no need to pass through analyzer
    95  	defaultVal, err := parse.StringToColumnDefaultValue(ctx, fmt.Sprintf(`"%s"`, diffTypeModified))
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	sqlSch = append(sqlSch, &sql.Column{
   101  		Name:     diffTypeColName,
   102  		Type:     sql.Text,
   103  		Default:  defaultVal,
   104  		Nullable: false,
   105  		Source:   diffTblName,
   106  	})
   107  
   108  	return &CommitDiffTable{
   109  		name:        tblName,
   110  		ddb:         ddb,
   111  		workingRoot: root,
   112  		ss:          ss,
   113  		joiner:      j,
   114  		sqlSch:      sqlSch,
   115  	}, nil
   116  }
   117  
   118  func calcSuperDuperSchema(ctx context.Context, ddb *doltdb.DoltDB, working *doltdb.RootValue, tblName string) (*schema.SuperSchema, error) {
   119  	refs, err := ddb.GetHeadRefs(ctx)
   120  
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	var superSchemas []*schema.SuperSchema
   126  	ss, found, err := working.GetSuperSchema(ctx, tblName)
   127  
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	if found {
   133  		superSchemas = append(superSchemas, ss)
   134  	}
   135  
   136  	for _, ref := range refs {
   137  		cm, err := ddb.ResolveCommitRef(ctx, ref)
   138  
   139  		if err != nil {
   140  			return nil, err
   141  		}
   142  
   143  		cmRoot, err := cm.GetRootValue()
   144  
   145  		if err != nil {
   146  			return nil, err
   147  		}
   148  
   149  		ss, found, err = cmRoot.GetSuperSchema(ctx, tblName)
   150  
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  
   155  		if found {
   156  			superSchemas = append(superSchemas, ss)
   157  		}
   158  	}
   159  
   160  	if len(superSchemas) == 0 {
   161  		return nil, sql.ErrTableNotFound.New(tblName)
   162  	}
   163  
   164  	superDuperSchema, err := schema.SuperSchemaUnion(superSchemas...)
   165  
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	return superDuperSchema, nil
   171  }
   172  
   173  func (dt *CommitDiffTable) Name() string {
   174  	return doltdb.DoltCommitDiffTablePrefix + dt.name
   175  }
   176  
   177  func (dt *CommitDiffTable) String() string {
   178  	return doltdb.DoltCommitDiffTablePrefix + dt.name
   179  }
   180  
   181  func (dt *CommitDiffTable) Schema() sql.Schema {
   182  	return dt.sqlSch
   183  }
   184  
   185  type SliceOfPartitionsItr struct {
   186  	partitions []sql.Partition
   187  	i          int
   188  	mu         *sync.Mutex
   189  }
   190  
   191  func NewSliceOfPartitionsItr(partitions []sql.Partition) *SliceOfPartitionsItr {
   192  	return &SliceOfPartitionsItr{
   193  		partitions: partitions,
   194  		mu:         &sync.Mutex{},
   195  	}
   196  }
   197  
   198  func (itr *SliceOfPartitionsItr) Next() (sql.Partition, error) {
   199  	itr.mu.Lock()
   200  	defer itr.mu.Unlock()
   201  
   202  	if itr.i >= len(itr.partitions) {
   203  		return nil, io.EOF
   204  	}
   205  
   206  	next := itr.partitions[itr.i]
   207  	itr.i++
   208  
   209  	return next, nil
   210  }
   211  
   212  func (itr *SliceOfPartitionsItr) Close(*sql.Context) error {
   213  	return nil
   214  }
   215  
   216  func (dt *CommitDiffTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
   217  	if dt.requiredFilterErr != nil {
   218  		return nil, fmt.Errorf("error querying table %s: %w", dt.Name(), dt.requiredFilterErr)
   219  	} else if dt.toCommitFilter == nil {
   220  		return nil, fmt.Errorf("error querying table %s: %w", dt.Name(), ErrExactlyOneToCommit)
   221  	} else if dt.fromCommitFilter == nil {
   222  		return nil, fmt.Errorf("error querying table %s: %w", dt.Name(), ErrExactlyOneFromCommit)
   223  	}
   224  
   225  	toRoot, toName, toDate, err := dt.rootValForFilter(ctx, dt.toCommitFilter)
   226  
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	fromRoot, fromName, fromDate, err := dt.rootValForFilter(ctx, dt.fromCommitFilter)
   232  
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  
   237  	toTable, _, err := toRoot.GetTable(ctx, dt.name)
   238  
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  
   243  	fromTable, _, err := fromRoot.GetTable(ctx, dt.name)
   244  
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  
   249  	return NewSliceOfPartitionsItr([]sql.Partition{diffPartition{
   250  		to:       toTable,
   251  		from:     fromTable,
   252  		toName:   toName,
   253  		fromName: fromName,
   254  		toDate:   toDate,
   255  		fromDate: fromDate,
   256  	}}), nil
   257  }
   258  
   259  func (dt *CommitDiffTable) rootValForFilter(ctx *sql.Context, eqFilter *expression.Equals) (*doltdb.RootValue, string, *types.Timestamp, error) {
   260  	gf, nonGF := eqFilter.Left(), eqFilter.Right()
   261  	if _, ok := gf.(*expression.GetField); !ok {
   262  		nonGF, gf = eqFilter.Left(), eqFilter.Right()
   263  	}
   264  
   265  	val, err := nonGF.Eval(ctx, nil)
   266  
   267  	if err != nil {
   268  		return nil, "", nil, err
   269  	}
   270  
   271  	hashStr, ok := val.(string)
   272  
   273  	if !ok {
   274  		return nil, "", nil, fmt.Errorf("received '%v' when expecting commit hash string", val)
   275  	}
   276  
   277  	var root *doltdb.RootValue
   278  	var commitTime *types.Timestamp
   279  	if strings.ToLower(hashStr) == "working" {
   280  		root = dt.workingRoot
   281  	} else {
   282  		cs, err := doltdb.NewCommitSpec(hashStr)
   283  
   284  		if err != nil {
   285  			return nil, "", nil, err
   286  		}
   287  
   288  		cm, err := dt.ddb.Resolve(ctx, cs, nil)
   289  
   290  		if err != nil {
   291  			return nil, "", nil, err
   292  		}
   293  
   294  		root, err = cm.GetRootValue()
   295  
   296  		if err != nil {
   297  			return nil, "", nil, err
   298  		}
   299  
   300  		meta, err := cm.GetCommitMeta()
   301  
   302  		if err != nil {
   303  			return nil, "", nil, err
   304  		}
   305  
   306  		t := meta.Time()
   307  		commitTime = (*types.Timestamp)(&t)
   308  	}
   309  
   310  	return root, hashStr, commitTime, nil
   311  }
   312  
   313  // HandledFilters returns the list of filters that will be handled by the table itself
   314  func (dt *CommitDiffTable) HandledFilters(filters []sql.Expression) []sql.Expression {
   315  	var commitFilters []sql.Expression
   316  	for _, filter := range filters {
   317  		isCommitFilter := false
   318  
   319  		if eqFilter, isEquality := filter.(*expression.Equals); isEquality {
   320  			for _, e := range []sql.Expression{eqFilter.Left(), eqFilter.Right()} {
   321  				if val, ok := e.(*expression.GetField); ok {
   322  					switch strings.ToLower(val.Name()) {
   323  					case toCommit:
   324  						if dt.toCommitFilter != nil {
   325  							dt.requiredFilterErr = ErrExactlyOneToCommit
   326  						}
   327  
   328  						isCommitFilter = true
   329  						dt.toCommitFilter = eqFilter
   330  					case fromCommit:
   331  						if dt.fromCommitFilter != nil {
   332  							dt.requiredFilterErr = ErrExactlyOneFromCommit
   333  						}
   334  
   335  						isCommitFilter = true
   336  						dt.fromCommitFilter = eqFilter
   337  					}
   338  				}
   339  			}
   340  		}
   341  
   342  		if isCommitFilter {
   343  			commitFilters = append(commitFilters, filter)
   344  		}
   345  	}
   346  
   347  	return commitFilters
   348  }
   349  
   350  // Filters returns the list of filters that are applied to this table.
   351  func (dt *CommitDiffTable) Filters() []sql.Expression {
   352  	if dt.toCommitFilter == nil || dt.fromCommitFilter == nil {
   353  		return nil
   354  	}
   355  
   356  	return []sql.Expression{dt.toCommitFilter, dt.fromCommitFilter}
   357  }
   358  
   359  // WithFilters returns a new sql.Table instance with the filters applied
   360  func (dt *CommitDiffTable) WithFilters(ctx *sql.Context, filters []sql.Expression) sql.Table {
   361  	return dt
   362  }
   363  
   364  func (dt *CommitDiffTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) {
   365  	dp := part.(diffPartition)
   366  	return dp.getRowIter(ctx, dt.ddb, dt.ss, dt.joiner)
   367  }