From 82a7a32a36c2d44486c436a9081cdad60871c41f Mon Sep 17 00:00:00 2001 From: Nick Tobey Date: Tue, 7 Oct 2025 12:27:58 -0700 Subject: [PATCH] Revert refactor of dolt_docs table. --- .../doltcore/sqle/dtables/docs_table.go | 206 ++++++++++++++---- 1 file changed, 169 insertions(+), 37 deletions(-) diff --git a/go/libraries/doltcore/sqle/dtables/docs_table.go b/go/libraries/doltcore/sqle/dtables/docs_table.go index 08b6b179db..2267347bfb 100644 --- a/go/libraries/doltcore/sqle/dtables/docs_table.go +++ b/go/libraries/doltcore/sqle/dtables/docs_table.go @@ -15,12 +15,17 @@ package dtables import ( + "io" + "github.com/dolthub/go-mysql-server/sql" sqlTypes "github.com/dolthub/go-mysql-server/sql/types" "github.com/dolthub/vitess/go/sqltypes" "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle/index" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/resolve" + "github.com/dolthub/dolt/go/store/hash" ) var _ sql.Table = (*DocsTable)(nil) @@ -32,28 +37,25 @@ var _ sql.IndexAddressableTable = (*DocsTable)(nil) // DocsTable is the system table that stores Dolt docs, such as LICENSE and README. type DocsTable struct { - UserSpaceSystemTable + backingTable VersionableTable } // NewDocsTable creates a DocsTable func NewDocsTable(_ *sql.Context, backingTable VersionableTable) sql.Table { - return &DocsTable{ - UserSpaceSystemTable: UserSpaceSystemTable{ - backingTable: backingTable, - tableName: getDoltDocsTableName(), - schema: GetDocsSchema(), - }, - } + return &DocsTable{backingTable: backingTable} } // NewEmptyDocsTable creates a DocsTable func NewEmptyDocsTable(_ *sql.Context) sql.Table { - return &DocsTable{ - UserSpaceSystemTable: UserSpaceSystemTable{ - tableName: getDoltDocsTableName(), - schema: GetDocsSchema(), - }, - } + return &DocsTable{} +} + +func (dt *DocsTable) Name() string { + return doltdb.GetDocTableName() +} + +func (dt *DocsTable) String() string { + return doltdb.GetDocTableName() } const defaultStringsLen = 16383 / 16 @@ -69,27 +71,39 @@ func getDoltDocsSchema() sql.Schema { } } -func (dt *DocsTable) LockedToRoot(ctx *sql.Context, root doltdb.RootValue) (sql.IndexAddressableTable, error) { +// Schema is a sql.Table interface function that gets the sql.Schema of the dolt_docs system table. +func (dt *DocsTable) Schema() sql.Schema { + return GetDocsSchema() +} + +func (dt *DocsTable) Collation() sql.CollationID { + return sql.Collation_Default +} + +// Partitions is a sql.Table interface function that returns a partition of the data. +func (dt *DocsTable) Partitions(context *sql.Context) (sql.PartitionIter, error) { if dt.backingTable == nil { - return dt, nil + // no backing table; return an empty iter. + return index.SinglePartitionIterFromNomsMap(nil), nil } - backingTableLockedToRoot, err := dt.backingTable.LockedToRoot(ctx, root) - if err != nil { - return nil, err - } - return &docsTableAsOf{backingTableLockedToRoot}, nil + return dt.backingTable.Partitions(context) } func (dt *DocsTable) PartitionRows(ctx *sql.Context, partition sql.Partition) (sql.RowIter, error) { - underlyingIter, err := dt.UserSpaceSystemTable.PartitionRows(ctx, partition) - if err != nil { - return nil, err - } - return makeDoltDocRows(ctx, underlyingIter) -} + var rowIter sql.RowIter + if dt.backingTable == nil { + // no backing table; empty iter. + rowIter = sql.RowsToRowIter() + } else { + var err error + rowIter, err = dt.backingTable.PartitionRows(ctx, partition) -func makeDoltDocRows(ctx *sql.Context, underlyingIter sql.RowIter) (sql.RowIter, error) { - rows, err := sql.RowIterToRows(ctx, underlyingIter) + if err != nil && err != io.EOF { + return nil, err + } + } + + rows, err := sql.RowIterToRows(ctx, rowIter) if err != nil { return nil, err @@ -115,7 +129,99 @@ func makeDoltDocRows(ctx *sql.Context, underlyingIter sql.RowIter) (sql.RowIter, }) } - return sql.RowsToRowIter(rows...), nil + rowIter = sql.RowsToRowIter(rows...) + + return rowIter, nil +} + +// Replacer returns a RowReplacer for this table. The RowReplacer will have Insert and optionally Delete called once +// for each row, followed by a call to Close() when all rows have been processed. +func (dt *DocsTable) Replacer(ctx *sql.Context) sql.RowReplacer { + return newDocsWriter(dt) +} + +// Updater returns a RowUpdater for this table. The RowUpdater will have Update called once for each row to be +// updated, followed by a call to Close() when all rows have been processed. +func (dt *DocsTable) Updater(ctx *sql.Context) sql.RowUpdater { + return newDocsWriter(dt) +} + +// Inserter returns an Inserter for this table. The Inserter will get one call to Insert() for each row to be +// inserted, and will end with a call to Close() to finalize the insert operation. +func (dt *DocsTable) Inserter(*sql.Context) sql.RowInserter { + return newDocsWriter(dt) +} + +// Deleter returns a RowDeleter for this table. The RowDeleter will get one call to Delete for each row to be deleted, +// and will end with a call to Close() to finalize the delete operation. +func (dt *DocsTable) Deleter(*sql.Context) sql.RowDeleter { + return newDocsWriter(dt) +} + +func (dt *DocsTable) LockedToRoot(ctx *sql.Context, root doltdb.RootValue) (sql.IndexAddressableTable, error) { + if dt.backingTable == nil { + return dt, nil + } + return dt.backingTable.LockedToRoot(ctx, root) +} + +// IndexedAccess implements IndexAddressableTable, but DocsTables has no indexes. +// Thus, this should never be called. +func (dt *DocsTable) IndexedAccess(ctx *sql.Context, lookup sql.IndexLookup) sql.IndexedTable { + panic("Unreachable") +} + +// GetIndexes implements IndexAddressableTable, but DocsTables has no indexes. +func (dt *DocsTable) GetIndexes(ctx *sql.Context) ([]sql.Index, error) { + return nil, nil +} + +func (dt *DocsTable) PreciseMatch() bool { + return true +} + +var _ sql.RowReplacer = (*docsWriter)(nil) +var _ sql.RowUpdater = (*docsWriter)(nil) +var _ sql.RowInserter = (*docsWriter)(nil) +var _ sql.RowDeleter = (*docsWriter)(nil) + +type docsWriter struct { + it *DocsTable + errDuringStatementBegin error + prevHash *hash.Hash + tableWriter dsess.TableWriter +} + +func newDocsWriter(it *DocsTable) *docsWriter { + return &docsWriter{it, nil, nil, nil} +} + +// Insert inserts the row given, returning an error if it cannot. Insert will be called once for each row to process +// for the insert operation, which may involve many rows. After all rows in an operation have been processed, Close +// is called. +func (iw *docsWriter) Insert(ctx *sql.Context, r sql.Row) error { + if err := iw.errDuringStatementBegin; err != nil { + return err + } + return iw.tableWriter.Insert(ctx, r) +} + +// Update the given row. Provides both the old and new rows. +func (iw *docsWriter) Update(ctx *sql.Context, old sql.Row, new sql.Row) error { + if err := iw.errDuringStatementBegin; err != nil { + return err + } + return iw.tableWriter.Update(ctx, old, new) +} + +// Delete deletes the given row. Returns ErrDeleteRowNotFound if the row was not found. Delete will be called once for +// each row to process for the delete operation, which may involve many rows. After all rows have been processed, +// Close is called. +func (iw *docsWriter) Delete(ctx *sql.Context, r sql.Row) error { + if err := iw.errDuringStatementBegin; err != nil { + return err + } + return iw.tableWriter.Delete(ctx, r) } func getDoltDocsTableName() doltdb.TableName { @@ -125,14 +231,40 @@ func getDoltDocsTableName() doltdb.TableName { return doltdb.TableName{Name: doltdb.GetDocTableName()} } -type docsTableAsOf struct { - sql.IndexAddressableTable +// StatementBegin is called before the first operation of a statement. Integrators should mark the state of the data +// in some way that it may be returned to in the case of an error. +func (iw *docsWriter) StatementBegin(ctx *sql.Context) { + name := getDoltDocsTableName() + prevHash, tableWriter, err := createWriteableSystemTable(ctx, name, iw.it.Schema()) + if err != nil { + iw.errDuringStatementBegin = err + } + iw.prevHash = prevHash + iw.tableWriter = tableWriter } -func (dt *docsTableAsOf) PartitionRows(ctx *sql.Context, partition sql.Partition) (sql.RowIter, error) { - underlyingIter, err := dt.IndexAddressableTable.PartitionRows(ctx, partition) - if err != nil { - return nil, err +// DiscardChanges is called if a statement encounters an error, and all current changes since the statement beginning +// should be discarded. +func (iw *docsWriter) DiscardChanges(ctx *sql.Context, errorEncountered error) error { + if iw.tableWriter != nil { + return iw.tableWriter.DiscardChanges(ctx, errorEncountered) } - return makeDoltDocRows(ctx, underlyingIter) + return nil +} + +// StatementComplete is called after the last operation of the statement, indicating that it has successfully completed. +// The mark set in StatementBegin may be removed, and a new one should be created on the next StatementBegin. +func (iw *docsWriter) StatementComplete(ctx *sql.Context) error { + if iw.tableWriter != nil { + return iw.tableWriter.StatementComplete(ctx) + } + return nil +} + +// Close finalizes the delete operation, persisting the result. +func (iw docsWriter) Close(ctx *sql.Context) error { + if iw.tableWriter != nil { + return iw.tableWriter.Close(ctx) + } + return nil }