github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/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  	"fmt"
    19  	"io"
    20  
    21  	"github.com/dolthub/go-mysql-server/sql"
    22  	"github.com/dolthub/go-mysql-server/sql/types"
    23  
    24  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
    29  )
    30  
    31  const branchesDefaultRowCount = 10
    32  
    33  var _ sql.Table = (*BranchesTable)(nil)
    34  var _ sql.StatisticsTable = (*BranchesTable)(nil)
    35  var _ sql.UpdatableTable = (*BranchesTable)(nil)
    36  var _ sql.DeletableTable = (*BranchesTable)(nil)
    37  var _ sql.InsertableTable = (*BranchesTable)(nil)
    38  var _ sql.ReplaceableTable = (*BranchesTable)(nil)
    39  
    40  // BranchesTable is the system table that accesses branches
    41  type BranchesTable struct {
    42  	db     dsess.SqlDatabase
    43  	remote bool
    44  }
    45  
    46  // NewBranchesTable creates a BranchesTable
    47  func NewBranchesTable(_ *sql.Context, db dsess.SqlDatabase) sql.Table {
    48  	return &BranchesTable{db: db}
    49  }
    50  
    51  // NewRemoteBranchesTable creates a BranchesTable with only remote refs
    52  func NewRemoteBranchesTable(_ *sql.Context, ddb dsess.SqlDatabase) sql.Table {
    53  	return &BranchesTable{ddb, true}
    54  }
    55  
    56  func (bt *BranchesTable) DataLength(ctx *sql.Context) (uint64, error) {
    57  	numBytesPerRow := schema.SchemaAvgLength(bt.Schema())
    58  	numRows, _, err := bt.RowCount(ctx)
    59  	if err != nil {
    60  		return 0, err
    61  	}
    62  	return numBytesPerRow * numRows, nil
    63  }
    64  
    65  func (bt *BranchesTable) RowCount(_ *sql.Context) (uint64, bool, error) {
    66  	return branchesDefaultRowCount, false, nil
    67  }
    68  
    69  // Name is a sql.Table interface function which returns the name of the table which is defined by the constant
    70  // BranchesTableName
    71  func (bt *BranchesTable) Name() string {
    72  	if bt.remote {
    73  		return doltdb.RemoteBranchesTableName
    74  	}
    75  	return doltdb.BranchesTableName
    76  }
    77  
    78  // String is a sql.Table interface function which returns the name of the table which is defined by the constant
    79  // BranchesTableName
    80  func (bt *BranchesTable) String() string {
    81  	if bt.remote {
    82  		return doltdb.RemoteBranchesTableName
    83  	}
    84  	return doltdb.BranchesTableName
    85  }
    86  
    87  // Schema is a sql.Table interface function that gets the sql.Schema of the branches system table
    88  func (bt *BranchesTable) Schema() sql.Schema {
    89  	tableName := doltdb.BranchesTableName
    90  	if bt.remote {
    91  		tableName = doltdb.RemoteBranchesTableName
    92  	}
    93  
    94  	columns := []*sql.Column{
    95  		{Name: "name", Type: types.Text, Source: tableName, PrimaryKey: true, Nullable: false, DatabaseSource: bt.db.Name()},
    96  		{Name: "hash", Type: types.Text, Source: tableName, PrimaryKey: false, Nullable: false, DatabaseSource: bt.db.Name()},
    97  		{Name: "latest_committer", Type: types.Text, Source: tableName, PrimaryKey: false, Nullable: true, DatabaseSource: bt.db.Name()},
    98  		{Name: "latest_committer_email", Type: types.Text, Source: tableName, PrimaryKey: false, Nullable: true, DatabaseSource: bt.db.Name()},
    99  		{Name: "latest_commit_date", Type: types.Datetime, Source: tableName, PrimaryKey: false, Nullable: true, DatabaseSource: bt.db.Name()},
   100  		{Name: "latest_commit_message", Type: types.Text, Source: tableName, PrimaryKey: false, Nullable: true, DatabaseSource: bt.db.Name()},
   101  	}
   102  	if !bt.remote {
   103  		columns = append(columns, &sql.Column{Name: "remote", Type: types.Text, Source: tableName, PrimaryKey: false, Nullable: true})
   104  		columns = append(columns, &sql.Column{Name: "branch", Type: types.Text, Source: tableName, PrimaryKey: false, Nullable: true})
   105  	}
   106  	return columns
   107  }
   108  
   109  // Collation implements the sql.Table interface.
   110  func (bt *BranchesTable) Collation() sql.CollationID {
   111  	return sql.Collation_Default
   112  }
   113  
   114  // Partitions is a sql.Table interface function that returns a partition of the data.  Currently the data is unpartitioned.
   115  func (bt *BranchesTable) Partitions(*sql.Context) (sql.PartitionIter, error) {
   116  	return index.SinglePartitionIterFromNomsMap(nil), nil
   117  }
   118  
   119  // PartitionRows is a sql.Table interface function that gets a row iterator for a partition
   120  func (bt *BranchesTable) PartitionRows(sqlCtx *sql.Context, part sql.Partition) (sql.RowIter, error) {
   121  	return NewBranchItr(sqlCtx, bt)
   122  }
   123  
   124  // BranchItr is a sql.RowItr implementation which iterates over each commit as if it's a row in the table.
   125  type BranchItr struct {
   126  	table    *BranchesTable
   127  	branches []string
   128  	commits  []*doltdb.Commit
   129  	idx      int
   130  }
   131  
   132  // NewBranchItr creates a BranchItr from the current environment.
   133  func NewBranchItr(ctx *sql.Context, table *BranchesTable) (*BranchItr, error) {
   134  	var branchRefs []ref.DoltRef
   135  	var err error
   136  	db := table.db
   137  	remote := table.remote
   138  
   139  	txRoot, err := dsess.TransactionRoot(ctx, db)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	ddb := db.DbData().Ddb
   145  
   146  	if remote {
   147  		branchRefs, err = ddb.GetRefsOfTypeByNomsRoot(ctx, map[ref.RefType]struct{}{ref.RemoteRefType: {}}, txRoot)
   148  		if err != nil {
   149  			return nil, err
   150  		}
   151  	} else {
   152  		branchRefs, err = ddb.GetBranchesByNomsRoot(ctx, txRoot)
   153  		if err != nil {
   154  			return nil, err
   155  		}
   156  	}
   157  
   158  	branchNames := make([]string, len(branchRefs))
   159  	commits := make([]*doltdb.Commit, len(branchRefs))
   160  	for i, branch := range branchRefs {
   161  		commit, err := ddb.ResolveCommitRefAtRoot(ctx, branch, txRoot)
   162  
   163  		if err != nil {
   164  			return nil, err
   165  		}
   166  
   167  		if branch.GetType() == ref.RemoteRefType {
   168  			branchNames[i] = "remotes/" + branch.GetPath()
   169  		} else {
   170  			branchNames[i] = branch.GetPath()
   171  		}
   172  
   173  		commits[i] = commit
   174  	}
   175  
   176  	return &BranchItr{
   177  		table:    table,
   178  		branches: branchNames,
   179  		commits:  commits,
   180  		idx:      0,
   181  	}, nil
   182  }
   183  
   184  // Next retrieves the next row. It will return io.EOF if it's the last row.
   185  // After retrieving the last row, Close will be automatically closed.
   186  func (itr *BranchItr) Next(ctx *sql.Context) (sql.Row, error) {
   187  	if itr.idx >= len(itr.commits) {
   188  		return nil, io.EOF
   189  	}
   190  
   191  	defer func() {
   192  		itr.idx++
   193  	}()
   194  
   195  	name := itr.branches[itr.idx]
   196  	cm := itr.commits[itr.idx]
   197  	meta, err := cm.GetCommitMeta(ctx)
   198  
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	h, err := cm.HashOf()
   204  
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	remoteBranches := itr.table.remote
   210  	if remoteBranches {
   211  		return sql.NewRow(name, h.String(), meta.Name, meta.Email, meta.Time(), meta.Description), nil
   212  	} else {
   213  		branches, err := itr.table.db.DbData().Rsr.GetBranches()
   214  
   215  		if err != nil {
   216  			return nil, err
   217  		}
   218  
   219  		remoteName := ""
   220  		branchName := ""
   221  		branch, ok := branches.Get(name)
   222  		if ok {
   223  			remoteName = branch.Remote
   224  			branchName = branch.Merge.Ref.GetPath()
   225  		}
   226  		return sql.NewRow(name, h.String(), meta.Name, meta.Email, meta.Time(), meta.Description, remoteName, branchName), nil
   227  	}
   228  }
   229  
   230  // Close closes the iterator.
   231  func (itr *BranchItr) Close(*sql.Context) error {
   232  	return nil
   233  }
   234  
   235  // Replacer returns a RowReplacer for this table. The RowReplacer will have Insert and optionally Delete called once
   236  // for each row, followed by a call to Close() when all rows have been processed.
   237  func (bt *BranchesTable) Replacer(ctx *sql.Context) sql.RowReplacer {
   238  	return branchWriter{bt}
   239  }
   240  
   241  // Updater returns a RowUpdater for this table. The RowUpdater will have Update called once for each row to be
   242  // updated, followed by a call to Close() when all rows have been processed.
   243  func (bt *BranchesTable) Updater(ctx *sql.Context) sql.RowUpdater {
   244  	return branchWriter{bt}
   245  }
   246  
   247  // Inserter returns an Inserter for this table. The Inserter will get one call to Insert() for each row to be
   248  // inserted, and will end with a call to Close() to finalize the insert operation.
   249  func (bt *BranchesTable) Inserter(*sql.Context) sql.RowInserter {
   250  	return branchWriter{bt}
   251  }
   252  
   253  // Deleter returns a RowDeleter for this table. The RowDeleter will get one call to Delete for each row to be deleted,
   254  // and will end with a call to Close() to finalize the delete operation.
   255  func (bt *BranchesTable) Deleter(*sql.Context) sql.RowDeleter {
   256  	return branchWriter{bt}
   257  }
   258  
   259  var _ sql.RowReplacer = branchWriter{nil}
   260  var _ sql.RowUpdater = branchWriter{nil}
   261  var _ sql.RowInserter = branchWriter{nil}
   262  var _ sql.RowDeleter = branchWriter{nil}
   263  
   264  type branchWriter struct {
   265  	bt *BranchesTable
   266  }
   267  
   268  // Insert inserts the row given, returning an error if it cannot. Insert will be called once for each row to process
   269  // for the insert operation, which may involve many rows. After all rows in an operation have been processed, Close
   270  // is called.
   271  func (bWr branchWriter) Insert(ctx *sql.Context, r sql.Row) error {
   272  	return fmt.Errorf("the dolt_branches table is read-only; use the dolt_branch stored procedure to edit remotes")
   273  }
   274  
   275  // Update the given row. Provides both the old and new rows.
   276  func (bWr branchWriter) Update(ctx *sql.Context, old sql.Row, new sql.Row) error {
   277  	return fmt.Errorf("the dolt_branches table is read-only; use the dolt_branch stored procedure to edit remotes")
   278  }
   279  
   280  // Delete deletes the given row. Returns ErrDeleteRowNotFound if the row was not found. Delete will be called once for
   281  // each row to process for the delete operation, which may involve many rows. After all rows have been processed,
   282  // Close is called.
   283  func (bWr branchWriter) Delete(ctx *sql.Context, r sql.Row) error {
   284  	return fmt.Errorf("the dolt_branches table is read-only; use the dolt_branch stored procedure to edit remotes")
   285  }
   286  
   287  // StatementBegin implements the interface sql.TableEditor. Currently a no-op.
   288  func (bWr branchWriter) StatementBegin(ctx *sql.Context) {}
   289  
   290  // DiscardChanges implements the interface sql.TableEditor. Currently a no-op.
   291  func (bWr branchWriter) DiscardChanges(ctx *sql.Context, errorEncountered error) error {
   292  	return nil
   293  }
   294  
   295  // StatementComplete implements the interface sql.TableEditor. Currently a no-op.
   296  func (bWr branchWriter) StatementComplete(ctx *sql.Context) error {
   297  	return nil
   298  }
   299  
   300  // Close finalizes the delete operation, persisting the result.
   301  func (bWr branchWriter) Close(*sql.Context) error {
   302  	return nil
   303  }