github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/dtables/log_table.go (about) 1 // Copyright 2019 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 "context" 19 "fmt" 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/doltdb" 25 "github.com/dolthub/dolt/go/libraries/doltcore/env/actions/commitwalk" 26 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 27 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/index" 28 "github.com/dolthub/dolt/go/store/hash" 29 "github.com/dolthub/dolt/go/store/prolly" 30 ) 31 32 const logsDefaultRowCount = 100 33 34 // LogTable is a sql.Table implementation that implements a system table which shows the dolt commit log 35 type LogTable struct { 36 dbName string 37 ddb *doltdb.DoltDB 38 head *doltdb.Commit 39 headHash hash.Hash 40 headCommitClosure *prolly.CommitClosure 41 } 42 43 var _ sql.Table = (*LogTable)(nil) 44 var _ sql.StatisticsTable = (*LogTable)(nil) 45 var _ sql.IndexAddressable = (*LogTable)(nil) 46 47 // NewLogTable creates a LogTable 48 func NewLogTable(_ *sql.Context, dbName string, ddb *doltdb.DoltDB, head *doltdb.Commit) sql.Table { 49 return &LogTable{dbName: dbName, ddb: ddb, head: head} 50 } 51 52 // DataLength implements sql.StatisticsTable 53 func (dt *LogTable) DataLength(ctx *sql.Context) (uint64, error) { 54 numBytesPerRow := schema.SchemaAvgLength(dt.Schema()) 55 numRows, _, err := dt.RowCount(ctx) 56 if err != nil { 57 return 0, err 58 } 59 return numBytesPerRow * numRows, nil 60 } 61 62 // RowCount implements sql.StatisticsTable 63 func (dt *LogTable) RowCount(ctx *sql.Context) (uint64, bool, error) { 64 cc, err := dt.head.GetCommitClosure(ctx) 65 if err != nil { 66 // TODO: remove this when we deprecate LD 67 return logsDefaultRowCount, false, nil 68 } 69 if cc.IsEmpty() { 70 return 1, true, nil 71 } 72 cnt, err := cc.Count() 73 return uint64(cnt + 1), true, err 74 } 75 76 // Name is a sql.Table interface function which returns the name of the table which is defined by the constant 77 // LogTableName 78 func (dt *LogTable) Name() string { 79 return doltdb.LogTableName 80 } 81 82 // String is a sql.Table interface function which returns the name of the table which is defined by the constant 83 // LogTableName 84 func (dt *LogTable) String() string { 85 return doltdb.LogTableName 86 } 87 88 // Schema is a sql.Table interface function that gets the sql.Schema of the log system table. 89 func (dt *LogTable) Schema() sql.Schema { 90 return []*sql.Column{ 91 {Name: "commit_hash", Type: types.Text, Source: doltdb.LogTableName, PrimaryKey: true, DatabaseSource: dt.dbName}, 92 {Name: "committer", Type: types.Text, Source: doltdb.LogTableName, PrimaryKey: false, DatabaseSource: dt.dbName}, 93 {Name: "email", Type: types.Text, Source: doltdb.LogTableName, PrimaryKey: false, DatabaseSource: dt.dbName}, 94 {Name: "date", Type: types.Datetime, Source: doltdb.LogTableName, PrimaryKey: false, DatabaseSource: dt.dbName}, 95 {Name: "message", Type: types.Text, Source: doltdb.LogTableName, PrimaryKey: false, DatabaseSource: dt.dbName}, 96 } 97 } 98 99 // Collation implements the sql.Table interface. 100 func (dt *LogTable) Collation() sql.CollationID { 101 return sql.Collation_Default 102 } 103 104 // Partitions is a sql.Table interface function that returns a partition of the data. Currently the data is unpartitioned. 105 func (dt *LogTable) Partitions(*sql.Context) (sql.PartitionIter, error) { 106 return index.SinglePartitionIterFromNomsMap(nil), nil 107 } 108 109 // PartitionRows is a sql.Table interface function that gets a row iterator for a partition 110 func (dt *LogTable) PartitionRows(ctx *sql.Context, p sql.Partition) (sql.RowIter, error) { 111 switch p := p.(type) { 112 case *doltdb.CommitPart: 113 return sql.RowsToRowIter(sql.NewRow(p.Hash().String(), p.Meta().Name, p.Meta().Email, p.Meta().Time(), p.Meta().Description)), nil 114 default: 115 return NewLogItr(ctx, dt.ddb, dt.head) 116 } 117 } 118 119 func (dt *LogTable) GetIndexes(ctx *sql.Context) ([]sql.Index, error) { 120 return index.DoltCommitIndexes(dt.dbName, dt.Name(), dt.ddb, true) 121 } 122 123 // IndexedAccess implements sql.IndexAddressable 124 func (dt *LogTable) IndexedAccess(lookup sql.IndexLookup) sql.IndexedTable { 125 nt := *dt 126 return &nt 127 } 128 129 // PreciseMatch implements sql.IndexAddressable 130 func (dt *LogTable) PreciseMatch() bool { 131 return true 132 } 133 134 func (dt *LogTable) LookupPartitions(ctx *sql.Context, lookup sql.IndexLookup) (sql.PartitionIter, error) { 135 if lookup.Index.ID() == index.CommitHashIndexId { 136 return dt.commitHashPartitionIter(ctx, lookup) 137 } 138 139 return dt.Partitions(ctx) 140 } 141 142 func (dt *LogTable) commitHashPartitionIter(ctx *sql.Context, lookup sql.IndexLookup) (sql.PartitionIter, error) { 143 hashStrs, ok := index.LookupToPointSelectStr(lookup) 144 if !ok { 145 return nil, fmt.Errorf("failed to parse commit lookup ranges: %s", sql.DebugString(lookup.Ranges)) 146 } 147 hashes, commits, metas := index.HashesToCommits(ctx, dt.ddb, hashStrs, nil, false) 148 if len(hashes) == 0 { 149 return sql.PartitionsToPartitionIter(), nil 150 } 151 var partitions []sql.Partition 152 for i, h := range hashes { 153 height, err := commits[i].Height() 154 if err != nil { 155 return nil, err 156 } 157 158 ok, err = dt.CommitIsInScope(ctx, height, h) 159 if err != nil { 160 return nil, err 161 } 162 if !ok { 163 continue 164 } 165 166 partitions = append(partitions, doltdb.NewCommitPart(h, commits[i], metas[i])) 167 168 } 169 return sql.PartitionsToPartitionIter(partitions...), nil 170 } 171 172 // CommitIsInScope returns true if a given commit hash is head or is 173 // visible from the current head's ancestry graph. 174 func (dt *LogTable) CommitIsInScope(ctx context.Context, height uint64, h hash.Hash) (bool, error) { 175 headHash, err := dt.HeadHash() 176 if err != nil { 177 return false, err 178 } 179 if headHash == h { 180 return true, nil 181 } 182 cc, err := dt.HeadCommitClosure(ctx) 183 if err != nil { 184 return false, err 185 } 186 return cc.ContainsKey(ctx, h, height) 187 } 188 189 func (dt *LogTable) HeadCommitClosure(ctx context.Context) (*prolly.CommitClosure, error) { 190 if dt.headCommitClosure == nil { 191 cc, err := dt.head.GetCommitClosure(ctx) 192 dt.headCommitClosure = &cc 193 if err != nil { 194 return nil, err 195 } 196 } 197 return dt.headCommitClosure, nil 198 } 199 200 func (dt *LogTable) HeadHash() (hash.Hash, error) { 201 if dt.headHash.IsEmpty() { 202 var err error 203 dt.headHash, err = dt.head.HashOf() 204 if err != nil { 205 return hash.Hash{}, err 206 } 207 } 208 return dt.headHash, nil 209 } 210 211 // LogItr is a sql.RowItr implementation which iterates over each commit as if it's a row in the table. 212 type LogItr struct { 213 child doltdb.CommitItr 214 } 215 216 // NewLogItr creates a LogItr from the current environment. 217 func NewLogItr(ctx *sql.Context, ddb *doltdb.DoltDB, head *doltdb.Commit) (*LogItr, error) { 218 h, err := head.HashOf() 219 if err != nil { 220 return nil, err 221 } 222 223 child, err := commitwalk.GetTopologicalOrderIterator(ctx, ddb, []hash.Hash{h}, nil) 224 if err != nil { 225 return nil, err 226 } 227 228 return &LogItr{child}, nil 229 } 230 231 // Next retrieves the next row. It will return io.EOF if it's the last row. 232 // After retrieving the last row, Close will be automatically closed. 233 func (itr *LogItr) Next(ctx *sql.Context) (sql.Row, error) { 234 h, optCmt, err := itr.child.Next(ctx) 235 if err != nil { 236 return nil, err 237 } 238 239 cm, ok := optCmt.ToCommit() 240 if !ok { 241 // Should have been caught by the commit walk. 242 return nil, doltdb.ErrGhostCommitRuntimeFailure 243 } 244 245 meta, err := cm.GetCommitMeta(ctx) 246 if err != nil { 247 return nil, err 248 } 249 250 return sql.NewRow(h.String(), meta.Name, meta.Email, meta.Time(), meta.Description), nil 251 } 252 253 // Close closes the iterator. 254 func (itr *LogItr) Close(*sql.Context) error { 255 return nil 256 }