github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/dtables/branches_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  	"io"
    20  
    21  	"github.com/dolthub/go-mysql-server/sql"
    22  
    23  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    24  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    26  	"github.com/dolthub/dolt/go/store/types"
    27  )
    28  
    29  var _ sql.Table = (*BranchesTable)(nil)
    30  var _ sql.UpdatableTable = (*BranchesTable)(nil)
    31  var _ sql.DeletableTable = (*BranchesTable)(nil)
    32  var _ sql.InsertableTable = (*BranchesTable)(nil)
    33  var _ sql.ReplaceableTable = (*BranchesTable)(nil)
    34  
    35  // BranchesTable is a sql.Table implementation that implements a system table which shows the dolt branches
    36  type BranchesTable struct {
    37  	ddb *doltdb.DoltDB
    38  }
    39  
    40  // NewBranchesTable creates a BranchesTable
    41  func NewBranchesTable(_ *sql.Context, ddb *doltdb.DoltDB) sql.Table {
    42  	return &BranchesTable{ddb}
    43  }
    44  
    45  // Name is a sql.Table interface function which returns the name of the table which is defined by the constant
    46  // BranchesTableName
    47  func (bt *BranchesTable) Name() string {
    48  	return doltdb.BranchesTableName
    49  }
    50  
    51  // String is a sql.Table interface function which returns the name of the table which is defined by the constant
    52  // BranchesTableName
    53  func (bt *BranchesTable) String() string {
    54  	return doltdb.BranchesTableName
    55  }
    56  
    57  // Schema is a sql.Table interface function that gets the sql.Schema of the branches system table
    58  func (bt *BranchesTable) Schema() sql.Schema {
    59  	return []*sql.Column{
    60  		{Name: "name", Type: sql.Text, Source: doltdb.BranchesTableName, PrimaryKey: true, Nullable: false},
    61  		{Name: "hash", Type: sql.Text, Source: doltdb.BranchesTableName, PrimaryKey: false, Nullable: false},
    62  		{Name: "latest_committer", Type: sql.Text, Source: doltdb.BranchesTableName, PrimaryKey: false, Nullable: true},
    63  		{Name: "latest_committer_email", Type: sql.Text, Source: doltdb.BranchesTableName, PrimaryKey: false, Nullable: true},
    64  		{Name: "latest_commit_date", Type: sql.Datetime, Source: doltdb.BranchesTableName, PrimaryKey: false, Nullable: true},
    65  		{Name: "latest_commit_message", Type: sql.Text, Source: doltdb.BranchesTableName, PrimaryKey: false, Nullable: true},
    66  	}
    67  }
    68  
    69  // Partitions is a sql.Table interface function that returns a partition of the data.  Currently the data is unpartitioned.
    70  func (bt *BranchesTable) Partitions(*sql.Context) (sql.PartitionIter, error) {
    71  	return sqlutil.NewSinglePartitionIter(types.Map{}), nil
    72  }
    73  
    74  // PartitionRows is a sql.Table interface function that gets a row iterator for a partition
    75  func (bt *BranchesTable) PartitionRows(sqlCtx *sql.Context, part sql.Partition) (sql.RowIter, error) {
    76  	return NewBranchItr(sqlCtx, bt.ddb)
    77  }
    78  
    79  // BranchItr is a sql.RowItr implementation which iterates over each commit as if it's a row in the table.
    80  type BranchItr struct {
    81  	branches []string
    82  	commits  []*doltdb.Commit
    83  	idx      int
    84  }
    85  
    86  // NewBranchItr creates a BranchItr from the current environment.
    87  func NewBranchItr(sqlCtx *sql.Context, ddb *doltdb.DoltDB) (*BranchItr, error) {
    88  	branches, err := ddb.GetBranches(sqlCtx)
    89  
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	branchNames := make([]string, len(branches))
    95  	commits := make([]*doltdb.Commit, len(branches))
    96  	for i, branch := range branches {
    97  		commit, err := ddb.ResolveCommitRef(sqlCtx, branch)
    98  
    99  		if err != nil {
   100  			return nil, err
   101  		}
   102  
   103  		branchNames[i] = branch.GetPath()
   104  		commits[i] = commit
   105  	}
   106  
   107  	return &BranchItr{branchNames, commits, 0}, nil
   108  }
   109  
   110  // Next retrieves the next row. It will return io.EOF if it's the last row.
   111  // After retrieving the last row, Close will be automatically closed.
   112  func (itr *BranchItr) Next() (sql.Row, error) {
   113  	if itr.idx >= len(itr.commits) {
   114  		return nil, io.EOF
   115  	}
   116  
   117  	defer func() {
   118  		itr.idx++
   119  	}()
   120  
   121  	name := itr.branches[itr.idx]
   122  	cm := itr.commits[itr.idx]
   123  	meta, err := cm.GetCommitMeta()
   124  
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	h, err := cm.HashOf()
   130  
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	return sql.NewRow(name, h.String(), meta.Name, meta.Email, meta.Time(), meta.Description), nil
   136  }
   137  
   138  // Close closes the iterator.
   139  func (itr *BranchItr) Close(*sql.Context) error {
   140  	return nil
   141  }
   142  
   143  // Replacer returns a RowReplacer for this table. The RowReplacer will have Insert and optionally Delete called once
   144  // for each row, followed by a call to Close() when all rows have been processed.
   145  func (bt *BranchesTable) Replacer(ctx *sql.Context) sql.RowReplacer {
   146  	return branchWriter{bt}
   147  }
   148  
   149  // Updater returns a RowUpdater for this table. The RowUpdater will have Update called once for each row to be
   150  // updated, followed by a call to Close() when all rows have been processed.
   151  func (bt *BranchesTable) Updater(ctx *sql.Context) sql.RowUpdater {
   152  	return branchWriter{bt}
   153  }
   154  
   155  // Inserter returns an Inserter for this table. The Inserter will get one call to Insert() for each row to be
   156  // inserted, and will end with a call to Close() to finalize the insert operation.
   157  func (bt *BranchesTable) Inserter(*sql.Context) sql.RowInserter {
   158  	return branchWriter{bt}
   159  }
   160  
   161  // Deleter returns a RowDeleter for this table. The RowDeleter will get one call to Delete for each row to be deleted,
   162  // and will end with a call to Close() to finalize the delete operation.
   163  func (bt *BranchesTable) Deleter(*sql.Context) sql.RowDeleter {
   164  	return branchWriter{bt}
   165  }
   166  
   167  var _ sql.RowReplacer = branchWriter{nil}
   168  var _ sql.RowUpdater = branchWriter{nil}
   169  var _ sql.RowInserter = branchWriter{nil}
   170  var _ sql.RowDeleter = branchWriter{nil}
   171  
   172  type branchWriter struct {
   173  	bt *BranchesTable
   174  }
   175  
   176  func branchAndHashFromRow(r sql.Row) (string, string, error) {
   177  	branchName, ok := r[0].(string)
   178  
   179  	if !ok {
   180  		return "", "", errors.New("invalid value type for branch")
   181  	} else if !doltdb.IsValidUserBranchName(branchName) {
   182  		return "", "", doltdb.ErrInvBranchName
   183  	}
   184  
   185  	commitHash, ok := r[1].(string)
   186  
   187  	if !ok {
   188  		return "", "", errors.New("invalid value type for hash")
   189  	}
   190  
   191  	return branchName, commitHash, nil
   192  }
   193  
   194  // Insert inserts the row given, returning an error if it cannot. Insert will be called once for each row to process
   195  // for the insert operation, which may involve many rows. After all rows in an operation have been processed, Close
   196  // is called.
   197  func (bWr branchWriter) Insert(ctx *sql.Context, r sql.Row) error {
   198  	branchName, commitHash, err := branchAndHashFromRow(r)
   199  
   200  	if err != nil {
   201  		return err
   202  	}
   203  
   204  	cs, err := doltdb.NewCommitSpec(commitHash)
   205  
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	ddb := bWr.bt.ddb
   211  	cm, err := ddb.Resolve(ctx, cs, nil)
   212  
   213  	if err != nil {
   214  		return err
   215  	}
   216  
   217  	branchRef := ref.NewBranchRef(branchName)
   218  	return ddb.NewBranchAtCommit(ctx, branchRef, cm)
   219  }
   220  
   221  // Update the given row. Provides both the old and new rows.
   222  func (bWr branchWriter) Update(ctx *sql.Context, old sql.Row, new sql.Row) error {
   223  	return bWr.Insert(ctx, new)
   224  }
   225  
   226  // Delete deletes the given row. Returns ErrDeleteRowNotFound if the row was not found. Delete will be called once for
   227  // each row to process for the delete operation, which may involve many rows. After all rows have been processed,
   228  // Close is called.
   229  func (bWr branchWriter) Delete(ctx *sql.Context, r sql.Row) error {
   230  	branchName, _, err := branchAndHashFromRow(r)
   231  
   232  	if err != nil {
   233  		return err
   234  	}
   235  
   236  	brRef := ref.NewBranchRef(branchName)
   237  	exists, err := bWr.bt.ddb.HasRef(ctx, brRef)
   238  
   239  	if err != nil {
   240  		return err
   241  	}
   242  
   243  	if !exists {
   244  		return sql.ErrDeleteRowNotFound.New()
   245  	}
   246  
   247  	return bWr.bt.ddb.DeleteBranch(ctx, brRef)
   248  }
   249  
   250  // StatementBegin implements the interface sql.TableEditor. Currently a no-op.
   251  func (bWr branchWriter) StatementBegin(ctx *sql.Context) {}
   252  
   253  // DiscardChanges implements the interface sql.TableEditor. Currently a no-op.
   254  func (bWr branchWriter) DiscardChanges(ctx *sql.Context, errorEncountered error) error {
   255  	return nil
   256  }
   257  
   258  // StatementComplete implements the interface sql.TableEditor. Currently a no-op.
   259  func (bWr branchWriter) StatementComplete(ctx *sql.Context) error {
   260  	return nil
   261  }
   262  
   263  // Close finalizes the delete operation, persisting the result.
   264  func (bWr branchWriter) Close(*sql.Context) error {
   265  	return nil
   266  }