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 }