github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/dtables/conflicts_tables.go (about) 1 package dtables 2 3 // Copyright 2019 Dolthub, Inc. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 import ( 18 "errors" 19 "fmt" 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/merge" 25 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/index" 26 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil" 27 "github.com/dolthub/dolt/go/store/types" 28 ) 29 30 // NewConflictsTable returns a new ConflictsTable instance 31 func NewConflictsTable(ctx *sql.Context, tblName string, srcTbl sql.Table, root doltdb.RootValue, rs RootSetter) (sql.Table, error) { 32 tbl, tblName, ok, err := doltdb.GetTableInsensitive(ctx, root, tblName) 33 if err != nil { 34 return nil, err 35 } else if !ok { 36 return nil, sql.ErrTableNotFound.New(tblName) 37 } 38 39 if types.IsFormat_DOLT(tbl.Format()) { 40 upd, ok := srcTbl.(sql.UpdatableTable) 41 if !ok { 42 return nil, fmt.Errorf("%s can not have conflicts because it is not updateable", tblName) 43 } 44 return newProllyConflictsTable(ctx, tbl, upd, tblName, root, rs) 45 } 46 47 return newNomsConflictsTable(ctx, tbl, tblName, root, rs) 48 } 49 50 func newNomsConflictsTable(ctx *sql.Context, tbl *doltdb.Table, tblName string, root doltdb.RootValue, rs RootSetter) (sql.Table, error) { 51 rd, err := merge.NewConflictReader(ctx, tbl, tblName) 52 if err != nil { 53 return nil, err 54 } 55 confSch := rd.GetSchema() 56 57 sqlSch, err := sqlutil.FromDoltSchema("", doltdb.DoltConfTablePrefix+tblName, confSch) 58 if err != nil { 59 return nil, err 60 } 61 62 return ConflictsTable{ 63 tblName: tblName, 64 sqlSch: sqlSch, 65 root: root, 66 tbl: tbl, 67 rd: rd, 68 rs: rs, 69 }, nil 70 } 71 72 var _ sql.Table = ConflictsTable{} 73 var _ sql.DeletableTable = ConflictsTable{} 74 75 // ConflictsTable is a sql.Table implementation that provides access to the conflicts that exist for a user table 76 type ConflictsTable struct { 77 tblName string 78 sqlSch sql.PrimaryKeySchema 79 root doltdb.RootValue 80 tbl *doltdb.Table 81 rd *merge.ConflictReader 82 rs RootSetter 83 } 84 85 type RootSetter interface { 86 SetRoot(ctx *sql.Context, root doltdb.RootValue) error 87 } 88 89 // Name returns the name of the table 90 func (ct ConflictsTable) Name() string { 91 return doltdb.DoltConfTablePrefix + ct.tblName 92 } 93 94 // String returns a string identifying the table 95 func (ct ConflictsTable) String() string { 96 return doltdb.DoltConfTablePrefix + ct.tblName 97 } 98 99 // Schema returns the sql.Schema of the table 100 func (ct ConflictsTable) Schema() sql.Schema { 101 return ct.sqlSch.Schema 102 } 103 104 // Collation implements the sql.Table interface. 105 func (ct ConflictsTable) Collation() sql.CollationID { 106 return sql.Collation_Default 107 } 108 109 // Partitions returns a PartitionIter which can be used to get all the data partitions 110 func (ct ConflictsTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) { 111 return index.SinglePartitionIterFromNomsMap(nil), nil 112 } 113 114 // PartitionRows returns a RowIter for the given partition 115 func (ct ConflictsTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) { 116 // conflict reader must be reset each time partitionRows is called. 117 rd, err := merge.NewConflictReader(ctx, ct.tbl, ct.tblName) 118 if err != nil { 119 return nil, err 120 } 121 return conflictRowIter{rd}, nil 122 } 123 124 // Deleter returns a RowDeleter for this table. The RowDeleter will get one call to Delete for each row to be deleted, 125 // and will end with a call to Close() to finalize the delete operation. 126 func (ct ConflictsTable) Deleter(ctx *sql.Context) sql.RowDeleter { 127 return &conflictDeleter{ct: ct, rs: ct.rs} 128 } 129 130 type conflictRowIter struct { 131 rd *merge.ConflictReader 132 } 133 134 // Next retrieves the next row. It will return io.EOF if it's the last row. 135 // After retrieving the last row, Close will be automatically closed. 136 func (itr conflictRowIter) Next(ctx *sql.Context) (sql.Row, error) { 137 cnf, err := itr.rd.NextConflict(ctx) 138 139 if err != nil { 140 return nil, err 141 } 142 143 return sqlutil.DoltRowToSqlRow(cnf, itr.rd.GetSchema()) 144 } 145 146 // Close the iterator. 147 func (itr conflictRowIter) Close(*sql.Context) error { 148 return itr.rd.Close() 149 } 150 151 type conflictDeleter struct { 152 ct ConflictsTable 153 rs RootSetter 154 pks []types.Value 155 } 156 157 var _ sql.RowDeleter = &conflictDeleter{} 158 159 // Delete deletes the given row. Returns ErrDeleteRowNotFound if the row was not found. Delete will be called once for 160 // each row to process for the delete operation, which may involve many rows. After all rows have been processed, 161 // Close is called. 162 func (cd *conflictDeleter) Delete(ctx *sql.Context, r sql.Row) error { 163 cnfSch := cd.ct.rd.GetSchema() 164 // We could use a test VRW, but as any values which use VRWs will already exist, we can potentially save on memory usage 165 cnfRow, err := sqlutil.SqlRowToDoltRow(ctx, cd.ct.tbl.ValueReadWriter(), r, cnfSch) 166 167 if err != nil { 168 return err 169 } 170 171 pkVal, err := cd.ct.rd.GetKeyForConflict(ctx, cnfRow) 172 173 if err != nil { 174 return err 175 } 176 177 cd.pks = append(cd.pks, pkVal) 178 return nil 179 } 180 181 // StatementBegin implements the interface sql.TableEditor. Currently a no-op. 182 func (cd *conflictDeleter) StatementBegin(ctx *sql.Context) {} 183 184 // DiscardChanges implements the interface sql.TableEditor. Currently a no-op. 185 func (cd *conflictDeleter) DiscardChanges(ctx *sql.Context, errorEncountered error) error { 186 return nil 187 } 188 189 // StatementComplete implements the interface sql.TableEditor. Currently a no-op. 190 func (cd *conflictDeleter) StatementComplete(ctx *sql.Context) error { 191 return nil 192 } 193 194 // Close finalizes the delete operation, persisting the result. 195 func (cd *conflictDeleter) Close(ctx *sql.Context) error { 196 _, _, updatedTbl, err := cd.ct.tbl.ResolveConflicts(ctx, cd.pks) 197 198 if err != nil { 199 if errors.Is(err, doltdb.ErrNoConflictsResolved) { 200 return nil 201 } 202 203 return err 204 } 205 206 updatedRoot, err := cd.ct.root.PutTable(ctx, doltdb.TableName{Name: cd.ct.tblName}, updatedTbl) 207 208 if err != nil { 209 return err 210 } 211 212 return cd.rs.SetRoot(ctx, updatedRoot) 213 }