github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/dtables/commit_diff_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 "fmt" 20 "io" 21 "strings" 22 "sync" 23 24 "github.com/dolthub/go-mysql-server/sql" 25 26 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 27 "github.com/dolthub/dolt/go/libraries/doltcore/rowconv" 28 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 29 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/index" 30 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil" 31 "github.com/dolthub/dolt/go/store/types" 32 ) 33 34 const commitDiffDefaultRowCount = 1000 35 36 var ErrExactlyOneToCommit = errors.New("dolt_commit_diff_* tables must be filtered to a single 'to_commit'") 37 var ErrExactlyOneFromCommit = errors.New("dolt_commit_diff_* tables must be filtered to a single 'from_commit'") 38 var ErrInvalidCommitDiffTableArgs = errors.New("commit_diff_<table> requires one 'to_commit' and one 'from_commit'") 39 40 type CommitDiffTable struct { 41 name string 42 dbName string 43 ddb *doltdb.DoltDB 44 joiner *rowconv.Joiner 45 sqlSch sql.PrimaryKeySchema 46 workingRoot doltdb.RootValue 47 // toCommit and fromCommit are set via the 48 // sql.IndexAddressable interface 49 toCommit string 50 fromCommit string 51 requiredFilterErr error 52 targetSchema schema.Schema 53 } 54 55 var _ sql.Table = (*CommitDiffTable)(nil) 56 var _ sql.IndexAddressable = (*CommitDiffTable)(nil) 57 var _ sql.StatisticsTable = (*CommitDiffTable)(nil) 58 59 func NewCommitDiffTable(ctx *sql.Context, dbName, tblName string, ddb *doltdb.DoltDB, root doltdb.RootValue) (sql.Table, error) { 60 diffTblName := doltdb.DoltCommitDiffTablePrefix + tblName 61 62 table, _, ok, err := doltdb.GetTableInsensitive(ctx, root, tblName) 63 if err != nil { 64 return nil, err 65 } 66 if !ok { 67 return nil, sql.ErrTableNotFound.New(diffTblName) 68 } 69 70 sch, err := table.GetSchema(ctx) 71 if err != nil { 72 return nil, err 73 } 74 75 diffTableSchema, j, err := GetDiffTableSchemaAndJoiner(ddb.Format(), sch, sch) 76 if err != nil { 77 return nil, err 78 } 79 80 sqlSch, err := sqlutil.FromDoltSchema(dbName, diffTblName, diffTableSchema) 81 if err != nil { 82 return nil, err 83 } 84 85 return &CommitDiffTable{ 86 dbName: dbName, 87 name: tblName, 88 ddb: ddb, 89 workingRoot: root, 90 joiner: j, 91 sqlSch: sqlSch, 92 targetSchema: sch, 93 }, nil 94 } 95 96 func (dt *CommitDiffTable) DataLength(ctx *sql.Context) (uint64, error) { 97 numBytesPerRow := schema.SchemaAvgLength(dt.Schema()) 98 numRows, _, err := dt.RowCount(ctx) 99 if err != nil { 100 return 0, err 101 } 102 return numBytesPerRow * numRows, nil 103 } 104 105 func (dt *CommitDiffTable) RowCount(_ *sql.Context) (uint64, bool, error) { 106 return commitDiffDefaultRowCount, false, nil 107 } 108 109 func (dt *CommitDiffTable) Name() string { 110 return doltdb.DoltCommitDiffTablePrefix + dt.name 111 } 112 113 func (dt *CommitDiffTable) String() string { 114 return doltdb.DoltCommitDiffTablePrefix + dt.name 115 } 116 117 func (dt *CommitDiffTable) Schema() sql.Schema { 118 return dt.sqlSch.Schema 119 } 120 121 // Collation implements the sql.Table interface. 122 func (dt *CommitDiffTable) Collation() sql.CollationID { 123 return sql.Collation_Default 124 } 125 126 // GetIndexes implements sql.IndexAddressable 127 func (dt *CommitDiffTable) GetIndexes(ctx *sql.Context) ([]sql.Index, error) { 128 return []sql.Index{index.DoltToFromCommitIndex(dt.name)}, nil 129 } 130 131 // IndexedAccess implements sql.IndexAddressable 132 func (dt *CommitDiffTable) IndexedAccess(lookup sql.IndexLookup) sql.IndexedTable { 133 nt := *dt 134 return &nt 135 } 136 137 func (dt *CommitDiffTable) PreciseMatch() bool { 138 return false 139 } 140 141 // RequiredPredicates implements sql.IndexRequired 142 func (dt *CommitDiffTable) RequiredPredicates() []string { 143 return []string{"to_commit", "from_commit"} 144 } 145 146 func (dt *CommitDiffTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) { 147 return nil, fmt.Errorf("error querying table %s: %w", dt.Name(), ErrExactlyOneToCommit) 148 } 149 150 func (dt *CommitDiffTable) LookupPartitions(ctx *sql.Context, i sql.IndexLookup) (sql.PartitionIter, error) { 151 if len(i.Ranges) != 1 || len(i.Ranges[0]) != 2 { 152 return nil, ErrInvalidCommitDiffTableArgs 153 } 154 to := i.Ranges[0][0] 155 from := i.Ranges[0][1] 156 switch to.UpperBound.(type) { 157 case sql.Above, sql.Below: 158 default: 159 return nil, ErrInvalidCommitDiffTableArgs 160 } 161 switch from.UpperBound.(type) { 162 case sql.Above, sql.Below: 163 default: 164 return nil, ErrInvalidCommitDiffTableArgs 165 } 166 toCommit, _, err := to.Typ.Convert(sql.GetRangeCutKey(to.UpperBound)) 167 if err != nil { 168 return nil, err 169 } 170 var ok bool 171 dt.toCommit, ok = toCommit.(string) 172 if !ok { 173 return nil, fmt.Errorf("to_commit must be string, found %T", toCommit) 174 } 175 fromCommit, _, err := from.Typ.Convert(sql.GetRangeCutKey(from.UpperBound)) 176 if err != nil { 177 return nil, err 178 } 179 dt.fromCommit, ok = fromCommit.(string) 180 if !ok { 181 return nil, fmt.Errorf("from_commit must be string, found %T", fromCommit) 182 } 183 184 toRoot, toHash, toDate, err := dt.rootValForHash(ctx, dt.toCommit) 185 if err != nil { 186 return nil, err 187 } 188 189 fromRoot, fromHash, fromDate, err := dt.rootValForHash(ctx, dt.fromCommit) 190 if err != nil { 191 return nil, err 192 } 193 194 toTable, _, _, err := doltdb.GetTableInsensitive(ctx, toRoot, dt.name) 195 if err != nil { 196 return nil, err 197 } 198 199 fromTable, _, _, err := doltdb.GetTableInsensitive(ctx, fromRoot, dt.name) 200 if err != nil { 201 return nil, err 202 } 203 204 dp := DiffPartition{ 205 to: toTable, 206 from: fromTable, 207 toName: toHash, 208 fromName: fromHash, 209 toDate: toDate, 210 fromDate: fromDate, 211 toSch: dt.targetSchema, 212 fromSch: dt.targetSchema, 213 } 214 215 isDiffable, err := dp.isDiffablePartition(ctx) 216 if err != nil { 217 return nil, err 218 } 219 220 if !isDiffable { 221 ctx.Warn(PrimaryKeyChangeWarningCode, fmt.Sprintf(PrimaryKeyChangeWarning, dp.fromName, dp.toName)) 222 return NewSliceOfPartitionsItr([]sql.Partition{}), nil 223 } 224 225 return NewSliceOfPartitionsItr([]sql.Partition{dp}), nil 226 } 227 228 type SliceOfPartitionsItr struct { 229 partitions []sql.Partition 230 i int 231 mu *sync.Mutex 232 } 233 234 func NewSliceOfPartitionsItr(partitions []sql.Partition) *SliceOfPartitionsItr { 235 return &SliceOfPartitionsItr{ 236 partitions: partitions, 237 mu: &sync.Mutex{}, 238 } 239 } 240 241 func (itr *SliceOfPartitionsItr) Next(*sql.Context) (sql.Partition, error) { 242 itr.mu.Lock() 243 defer itr.mu.Unlock() 244 245 if itr.i >= len(itr.partitions) { 246 return nil, io.EOF 247 } 248 249 next := itr.partitions[itr.i] 250 itr.i++ 251 252 return next, nil 253 } 254 255 func (itr *SliceOfPartitionsItr) Close(*sql.Context) error { 256 return nil 257 } 258 259 func (dt *CommitDiffTable) rootValForHash(ctx *sql.Context, hashStr string) (doltdb.RootValue, string, *types.Timestamp, error) { 260 var root doltdb.RootValue 261 var commitTime *types.Timestamp 262 if strings.ToLower(hashStr) == "working" { 263 root = dt.workingRoot 264 } else { 265 cs, err := doltdb.NewCommitSpec(hashStr) 266 if err != nil { 267 return nil, "", nil, err 268 } 269 270 optCmt, err := dt.ddb.Resolve(ctx, cs, nil) 271 if err != nil { 272 return nil, "", nil, err 273 } 274 cm, ok := optCmt.ToCommit() 275 if !ok { 276 return nil, "", nil, doltdb.ErrGhostCommitEncountered 277 } 278 279 root, err = cm.GetRootValue(ctx) 280 281 if err != nil { 282 return nil, "", nil, err 283 } 284 285 meta, err := cm.GetCommitMeta(ctx) 286 287 if err != nil { 288 return nil, "", nil, err 289 } 290 291 t := meta.Time() 292 commitTime = (*types.Timestamp)(&t) 293 } 294 295 return root, hashStr, commitTime, nil 296 } 297 298 func (dt *CommitDiffTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) { 299 dp := part.(DiffPartition) 300 return dp.GetRowIter(ctx, dt.ddb, dt.joiner, sql.IndexLookup{}) 301 }