github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/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  
    23  	"github.com/dolthub/dolt/go/libraries/doltcore/diff"
    24  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/merge"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    28  	"github.com/dolthub/dolt/go/store/types"
    29  )
    30  
    31  // StatusTable is a sql.Table implementation that implements a system table which shows the dolt branches
    32  type StatusTable struct {
    33  	ddb *doltdb.DoltDB
    34  	rsr env.RepoStateReader
    35  	drw env.DocsReadWriter
    36  }
    37  
    38  func (s StatusTable) Name() string {
    39  	return doltdb.StatusTableName
    40  }
    41  
    42  func (s StatusTable) String() string {
    43  	return doltdb.StatusTableName
    44  }
    45  
    46  func (s StatusTable) Schema() sql.Schema {
    47  	return []*sql.Column{
    48  		{Name: "table_name", Type: sql.Text, Source: doltdb.StatusTableName, PrimaryKey: true, Nullable: false},
    49  		{Name: "staged", Type: sql.Boolean, Source: doltdb.StatusTableName, PrimaryKey: false, Nullable: false},
    50  		{Name: "status", Type: sql.Text, Source: doltdb.StatusTableName, PrimaryKey: false, Nullable: false},
    51  	}
    52  }
    53  
    54  func (s StatusTable) Partitions(*sql.Context) (sql.PartitionIter, error) {
    55  	return sqlutil.NewSinglePartitionIter(types.Map{}), nil
    56  }
    57  
    58  func (s StatusTable) PartitionRows(context *sql.Context, _ sql.Partition) (sql.RowIter, error) {
    59  	return newStatusItr(context, &s)
    60  }
    61  
    62  // NewStatusTable creates a StatusTable
    63  func NewStatusTable(_ *sql.Context, ddb *doltdb.DoltDB, rsr env.RepoStateReader, drw env.DocsReadWriter) sql.Table {
    64  	return &StatusTable{ddb: ddb, rsr: rsr, drw: drw}
    65  }
    66  
    67  // StatusIter is a sql.RowItr implementation which iterates over each commit as if it's a row in the table.
    68  type StatusItr struct {
    69  	tables   []string
    70  	isStaged []bool
    71  	statuses []string
    72  	idx      int
    73  }
    74  
    75  func newStatusItr(ctx *sql.Context, st *StatusTable) (*StatusItr, error) {
    76  	ddb := st.ddb
    77  	rsr := st.rsr
    78  	drw := st.drw
    79  
    80  	stagedTables, unstagedTables, err := diff.GetStagedUnstagedTableDeltas(ctx, ddb, rsr)
    81  
    82  	if err != nil {
    83  		return &StatusItr{}, err
    84  	}
    85  
    86  	stagedDocDiffs, unStagedDocDiffs, err := diff.GetDocDiffs(ctx, ddb, rsr, drw)
    87  
    88  	if err != nil {
    89  		return &StatusItr{}, err
    90  	}
    91  
    92  	workingTblsInConflict, _, _, err := merge.GetTablesInConflict(ctx, ddb, rsr)
    93  
    94  	if err != nil {
    95  		return &StatusItr{}, err
    96  	}
    97  
    98  	workingDocsInConflict, err := merge.GetDocsInConflict(ctx, ddb, rsr, drw)
    99  
   100  	if err != nil {
   101  		return &StatusItr{}, err
   102  	}
   103  
   104  	tLength := len(stagedTables) + len(unstagedTables) + len(stagedDocDiffs.Docs) + len(unStagedDocDiffs.Docs) + len(workingTblsInConflict) + len(workingDocsInConflict.Docs)
   105  
   106  	tables := make([]string, tLength)
   107  	isStaged := make([]bool, tLength)
   108  	statuses := make([]string, tLength)
   109  
   110  	itr := &StatusItr{tables: tables, isStaged: isStaged, statuses: statuses, idx: 0}
   111  
   112  	idx := handleStagedUnstagedTables(stagedTables, unstagedTables, itr, 0)
   113  	idx = handleStagedUnstagedDocDiffs(stagedDocDiffs, unStagedDocDiffs, itr, idx)
   114  	idx = handleWorkingTablesInConflict(workingTblsInConflict, itr, idx)
   115  	idx = handleWorkingDocConflicts(workingDocsInConflict, itr, idx)
   116  
   117  	return itr, nil
   118  }
   119  
   120  var tblDiffTypeToLabel = map[diff.TableDiffType]string{
   121  	diff.ModifiedTable: "modified",
   122  	diff.RenamedTable:  "renamed",
   123  	diff.RemovedTable:  "deleted",
   124  	diff.AddedTable:    "new table",
   125  }
   126  
   127  func handleStagedUnstagedTables(staged, unstaged []diff.TableDelta, itr *StatusItr, idx int) int {
   128  	combined := append(staged, unstaged...)
   129  	for i, td := range combined {
   130  		itr.isStaged[idx] = i < len(staged)
   131  		if td.IsAdd() {
   132  			itr.tables[idx] = td.CurName()
   133  			itr.statuses[idx] = tblDiffTypeToLabel[diff.AddedTable]
   134  		} else if td.IsDrop() {
   135  			itr.tables[idx] = td.CurName()
   136  			itr.statuses[idx] = tblDiffTypeToLabel[diff.RemovedTable]
   137  		} else if td.IsRename() {
   138  			itr.tables[idx] = fmt.Sprintf("%s -> %s", td.FromName, td.ToName)
   139  			itr.statuses[idx] = tblDiffTypeToLabel[diff.RemovedTable]
   140  		} else {
   141  			itr.tables[idx] = td.CurName()
   142  			itr.statuses[idx] = tblDiffTypeToLabel[diff.ModifiedTable]
   143  		}
   144  
   145  		idx += 1
   146  	}
   147  
   148  	return idx
   149  }
   150  
   151  var docDiffTypeToLabel = map[diff.DocDiffType]string{
   152  	diff.ModifiedDoc: "modified",
   153  	diff.RemovedDoc:  "deleted",
   154  	diff.AddedDoc:    "new doc",
   155  }
   156  
   157  func handleStagedUnstagedDocDiffs(staged *diff.DocDiffs, unstaged *diff.DocDiffs, itr *StatusItr, idx int) int {
   158  	combined := append(staged.Docs, unstaged.Docs...)
   159  	for i, docName := range combined {
   160  		dType := staged.DocToType[docName]
   161  
   162  		itr.tables[idx] = docName
   163  		itr.isStaged[idx] = i < len(staged.Docs)
   164  		itr.statuses[idx] = docDiffTypeToLabel[dType]
   165  
   166  		idx += 1
   167  	}
   168  
   169  	return idx
   170  }
   171  
   172  const mergeConflictStatus = "conflict"
   173  
   174  func handleWorkingTablesInConflict(workingTables []string, itr *StatusItr, idx int) int {
   175  	for _, tableName := range workingTables {
   176  		itr.tables[idx] = tableName
   177  		itr.isStaged[idx] = false
   178  		itr.statuses[idx] = mergeConflictStatus
   179  
   180  		idx += 1
   181  	}
   182  
   183  	return idx
   184  }
   185  
   186  func handleWorkingDocConflicts(workingDocs *diff.DocDiffs, itr *StatusItr, idx int) int {
   187  	for _, docName := range workingDocs.Docs {
   188  		itr.tables[idx] = docName
   189  		itr.isStaged[idx] = false
   190  		itr.statuses[idx] = mergeConflictStatus
   191  
   192  		idx += 1
   193  	}
   194  
   195  	return idx
   196  }
   197  
   198  // Next retrieves the next row. It will return io.EOF if it's the last row.
   199  // After retrieving the last row, Close will be automatically closed.
   200  func (itr *StatusItr) Next() (sql.Row, error) {
   201  	if itr.idx >= len(itr.tables) {
   202  		return nil, io.EOF
   203  	}
   204  
   205  	defer func() {
   206  		itr.idx++
   207  	}()
   208  
   209  	return sql.NewRow(itr.tables[itr.idx], itr.isStaged[itr.idx], itr.statuses[itr.idx]), nil
   210  }
   211  
   212  // Close closes the iterator.
   213  func (itr *StatusItr) Close(*sql.Context) error {
   214  	return nil
   215  }