github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/dtables/status_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/diff"
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
    29  )
    30  
    31  const statusDefaultRowCount = 10
    32  
    33  // StatusTable is a sql.Table implementation that implements a system table which shows the dolt branches
    34  type StatusTable struct {
    35  	ddb           *doltdb.DoltDB
    36  	workingSet    *doltdb.WorkingSet
    37  	rootsProvider env.RootsProvider
    38  }
    39  
    40  var _ sql.StatisticsTable = (*StatusTable)(nil)
    41  
    42  func (s StatusTable) DataLength(ctx *sql.Context) (uint64, error) {
    43  	numBytesPerRow := schema.SchemaAvgLength(s.Schema())
    44  	numRows, _, err := s.RowCount(ctx)
    45  	if err != nil {
    46  		return 0, err
    47  	}
    48  	return numBytesPerRow * numRows, nil
    49  }
    50  
    51  func (s StatusTable) RowCount(_ *sql.Context) (uint64, bool, error) {
    52  	return statusDefaultRowCount, false, nil
    53  }
    54  
    55  func (s StatusTable) Name() string {
    56  	return doltdb.StatusTableName
    57  }
    58  
    59  func (s StatusTable) String() string {
    60  	return doltdb.StatusTableName
    61  }
    62  
    63  func (s StatusTable) Schema() sql.Schema {
    64  	return []*sql.Column{
    65  		{Name: "table_name", Type: types.Text, Source: doltdb.StatusTableName, PrimaryKey: true, Nullable: false},
    66  		{Name: "staged", Type: types.Boolean, Source: doltdb.StatusTableName, PrimaryKey: true, Nullable: false},
    67  		{Name: "status", Type: types.Text, Source: doltdb.StatusTableName, PrimaryKey: true, Nullable: false},
    68  	}
    69  }
    70  
    71  func (s StatusTable) Collation() sql.CollationID {
    72  	return sql.Collation_Default
    73  }
    74  
    75  func (s StatusTable) Partitions(*sql.Context) (sql.PartitionIter, error) {
    76  	return index.SinglePartitionIterFromNomsMap(nil), nil
    77  }
    78  
    79  func (s StatusTable) PartitionRows(context *sql.Context, _ sql.Partition) (sql.RowIter, error) {
    80  	return newStatusItr(context, &s)
    81  }
    82  
    83  // NewStatusTable creates a StatusTable
    84  func NewStatusTable(_ *sql.Context, ddb *doltdb.DoltDB, ws *doltdb.WorkingSet, rp env.RootsProvider) sql.Table {
    85  	return &StatusTable{
    86  		ddb:           ddb,
    87  		workingSet:    ws,
    88  		rootsProvider: rp,
    89  	}
    90  }
    91  
    92  // StatusItr is a sql.RowIter implementation which iterates over each commit as if it's a row in the table.
    93  type StatusItr struct {
    94  	rows []statusTableRow
    95  }
    96  
    97  type statusTableRow struct {
    98  	tableName string
    99  	isStaged  bool
   100  	status    string
   101  }
   102  
   103  func newStatusItr(ctx *sql.Context, st *StatusTable) (*StatusItr, error) {
   104  	rp := st.rootsProvider
   105  
   106  	roots, err := rp.GetRoots(ctx)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	stagedTables, unstagedTables, err := diff.GetStagedUnstagedTableDeltas(ctx, roots)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	rows := make([]statusTableRow, 0, len(stagedTables)+len(unstagedTables))
   117  	for _, td := range stagedTables {
   118  		tblName := tableName(td)
   119  		if doltdb.IsFullTextTable(tblName) {
   120  			continue
   121  		}
   122  		rows = append(rows, statusTableRow{
   123  			tableName: tblName,
   124  			isStaged:  true,
   125  			status:    statusString(td),
   126  		})
   127  	}
   128  	for _, td := range unstagedTables {
   129  		tblName := tableName(td)
   130  		if doltdb.IsFullTextTable(tblName) {
   131  			continue
   132  		}
   133  		rows = append(rows, statusTableRow{
   134  			tableName: tblName,
   135  			isStaged:  false,
   136  			status:    statusString(td),
   137  		})
   138  	}
   139  
   140  	if st.workingSet.MergeActive() {
   141  		ms := st.workingSet.MergeState()
   142  		for _, tbl := range ms.TablesWithSchemaConflicts() {
   143  			rows = append(rows, statusTableRow{
   144  				tableName: tbl,
   145  				isStaged:  false,
   146  				status:    "schema conflict",
   147  			})
   148  		}
   149  
   150  		for _, tbl := range ms.MergedTables() {
   151  			rows = append(rows, statusTableRow{
   152  				tableName: tbl,
   153  				isStaged:  true,
   154  				status:    mergedStatus,
   155  			})
   156  		}
   157  	}
   158  
   159  	cnfTables, err := doltdb.TablesWithDataConflicts(ctx, roots.Working)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	for _, tbl := range cnfTables {
   164  		rows = append(rows, statusTableRow{
   165  			tableName: tbl,
   166  			status:    mergeConflictStatus,
   167  		})
   168  	}
   169  
   170  	return &StatusItr{rows: rows}, nil
   171  }
   172  
   173  func tableName(td diff.TableDelta) string {
   174  	if td.IsRename() {
   175  		return fmt.Sprintf("%s -> %s", td.FromName, td.ToName)
   176  	} else {
   177  		return td.CurName()
   178  	}
   179  }
   180  
   181  func statusString(td diff.TableDelta) string {
   182  	if td.IsAdd() {
   183  		return "new table"
   184  	} else if td.IsDrop() {
   185  		return "deleted"
   186  	} else if td.IsRename() {
   187  		return "renamed"
   188  	} else {
   189  		return "modified"
   190  	}
   191  }
   192  
   193  const mergeConflictStatus = "conflict"
   194  const mergedStatus = "merged"
   195  
   196  // Next retrieves the next row. It will return io.EOF if it's the last row.
   197  // After retrieving the last row, Close will be automatically closed.
   198  func (itr *StatusItr) Next(*sql.Context) (sql.Row, error) {
   199  	if len(itr.rows) <= 0 {
   200  		return nil, io.EOF
   201  	}
   202  	row := itr.rows[0]
   203  	itr.rows = itr.rows[1:]
   204  	return sql.NewRow(row.tableName, row.isStaged, row.status), nil
   205  }
   206  
   207  // Close closes the iterator.
   208  func (itr *StatusItr) Close(*sql.Context) error {
   209  	return nil
   210  }