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 }