Merge pull request #10227 from codeaucafe/codeaucafe/5862/make-ignore-system-table

dolthub/dolt#5862: Add ignore system table
This commit is contained in:
Nick Tobey
2026-01-22 00:41:21 -08:00
committed by GitHub
7 changed files with 544 additions and 64 deletions

View File

@@ -162,6 +162,7 @@ func GeneratedSystemTableNames() []string {
GetBranchActivityTableName(),
// [dtables.StatusTable] now uses [adapters.DoltTableAdapterRegistry] in its constructor for Doltgres.
StatusTableName,
StatusIgnoredTableName,
}
}
@@ -435,6 +436,9 @@ const (
// StatusTableName is the status system table name.
StatusTableName = "dolt_status"
// StatusIgnoredTableName is the status_ignored system table name.
StatusIgnoredTableName = "dolt_status_ignored"
// MergeStatusTableName is the merge status system table name.
MergeStatusTableName = "dolt_merge_status"
@@ -477,6 +481,7 @@ var DoltGeneratedTableNames = []string{
CommitsTableName,
CommitAncestorsTableName,
StatusTableName,
StatusIgnoredTableName,
MergeStatusTableName,
TagsTableName,
}

View File

@@ -758,59 +758,24 @@ func (db Database) getTableInsensitiveWithRoot(ctx *sql.Context, head *doltdb.Co
return nil, false, err
}
if !resolve.UseSearchPath || isDoltgresSystemTable {
sess := dsess.DSessFromSess(ctx.Session)
var rootsProvider env.RootsProvider[*sql.Context]
rootsProvider = dsess.NewSessionStateAdapter(
sess, db.RevisionQualifiedName(),
concurrentmap.New[string, env.Remote](),
concurrentmap.New[string, env.BranchConfig](),
concurrentmap.New[string, env.Remote]())
ws, err := sess.WorkingSet(ctx, db.RevisionQualifiedName())
rootsProvider, ws, err := getStatusTableRootsProvider(ctx, db, ds, asOf)
if err != nil {
return nil, false, err
}
asOf, ok := asOf.(string)
if !ok {
return nil, false, fmt.Errorf(
"unexpected type for asOf param: %T", asOf)
}
// If |asOf| is set, then we need to get the correct roots for the
// status table to use. We skip the special revision spec, HEAD,
// because it represents the current branch.
if asOf != "" && !strings.EqualFold(asOf, "HEAD") {
// If |asOf| is a branch name, then grab the working set for that
// branch and use its data.
ddb, ok := ds.GetDoltDB(ctx, ctx.GetCurrentDatabase())
if !ok {
return nil, false, fmt.Errorf(
"unable to get DoltDB for database %s", ctx.GetCurrentDatabase())
}
_, hasBranch, err := ddb.HasBranch(ctx, asOf)
if err != nil {
return nil, false, err
}
if hasBranch {
branchRoots, err := getRootsForBranch(ctx, ddb, asOf)
if err != nil {
return nil, false, err
}
rootsProvider = &staticRootsProvider{
roots: branchRoots,
}
} else {
// If this isn't a branch, then it's a tag or a commit, or a
// commit spec. In all of these cases, dolt_status will have
// no data, because there is no valid head/working/staged roots,
// so we provide a nil rootsProvider
rootsProvider = nil
}
}
dt, found = dtables.NewStatusTable(ctx, lwrName, db.ddb, ws, rootsProvider), true
}
case doltdb.StatusIgnoredTableName:
isDoltgresSystemTable, err := resolve.IsDoltgresSystemTable(ctx, tname, root)
if err != nil {
return nil, false, err
}
if !resolve.UseSearchPath || isDoltgresSystemTable {
rootsProvider, ws, err := getStatusTableRootsProvider(ctx, db, ds, asOf)
if err != nil {
return nil, false, err
}
dt, found = dtables.NewStatusIgnoredTable(ctx, lwrName, db.ddb, ws, rootsProvider), true
}
case doltdb.MergeStatusTableName, doltdb.GetMergeStatusTableName():
isDoltgresSystemTable, err := resolve.IsDoltgresSystemTable(ctx, tname, root)
if err != nil {
@@ -1296,6 +1261,68 @@ func (srp *staticRootsProvider) GetRoots(ctx *sql.Context) (doltdb.Roots, error)
var _ env.RootsProvider[*sql.Context] = (*staticRootsProvider)(nil)
// getStatusTableRootsProvider returns the roots provider and working set needed for status tables
// (dolt_status and dolt_status_ignored). This handles the asOf parameter to support querying status
// for different branches, tags, or commits.
func getStatusTableRootsProvider(
ctx *sql.Context,
db Database,
ds *dsess.DoltSession,
asOf interface{},
) (env.RootsProvider[*sql.Context], *doltdb.WorkingSet, error) {
sess := dsess.DSessFromSess(ctx.Session)
var rootsProvider env.RootsProvider[*sql.Context]
rootsProvider = dsess.NewSessionStateAdapter(
sess, db.RevisionQualifiedName(),
concurrentmap.New[string, env.Remote](),
concurrentmap.New[string, env.BranchConfig](),
concurrentmap.New[string, env.Remote]())
ws, err := sess.WorkingSet(ctx, db.RevisionQualifiedName())
if err != nil {
return nil, nil, err
}
asOfStr, ok := asOf.(string)
if !ok {
return nil, nil, fmt.Errorf("unexpected type for asOf param: %T", asOf)
}
// If |asOf| is set, then we need to get the correct roots for the
// status table to use. We skip the special revision spec, HEAD,
// because it represents the current branch.
if asOfStr != "" && !strings.EqualFold(asOfStr, "HEAD") {
// If |asOf| is a branch name, then grab the working set for that
// branch and use its data.
ddb, ok := ds.GetDoltDB(ctx, ctx.GetCurrentDatabase())
if !ok {
return nil, nil, fmt.Errorf(
"unable to get DoltDB for database %s", ctx.GetCurrentDatabase())
}
_, hasBranch, err := ddb.HasBranch(ctx, asOfStr)
if err != nil {
return nil, nil, err
}
if hasBranch {
branchRoots, err := getRootsForBranch(ctx, ddb, asOfStr)
if err != nil {
return nil, nil, err
}
rootsProvider = &staticRootsProvider{
roots: branchRoots,
}
} else {
// If this isn't a branch, then it's a tag or a commit, or a
// commit spec. In all of these cases, the status table will have
// no data, because there is no valid head/working/staged roots,
// so we provide a nil rootsProvider
rootsProvider = nil
}
}
return rootsProvider, ws, nil
}
// workingSetStagedRoot returns the staged root for the current session in the database
// named |dbName|. If a working set is not available (e.g. if a commit or tag is checked
// out), this function returns an ErrOperationNotSupportedInDetachedHead error.

View File

@@ -0,0 +1,204 @@
// Copyright 2026 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dtables
import (
"io"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/types"
"github.com/dolthub/dolt/go/libraries/doltcore/diff"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/adapters"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
)
const statusIgnoredDefaultRowCount = 10
// StatusIgnoredTable is a sql.Table implementation that shows status including ignored tables.
// This is the SQL equivalent of `dolt status --ignored`.
type StatusIgnoredTable struct {
rootsProvider env.RootsProvider[*sql.Context]
ddb *doltdb.DoltDB
workingSet *doltdb.WorkingSet
tableName string
}
var _ sql.StatisticsTable = (*StatusIgnoredTable)(nil)
func (st StatusIgnoredTable) DataLength(ctx *sql.Context) (uint64, error) {
numBytesPerRow := schema.SchemaAvgLength(st.Schema())
numRows, _, err := st.RowCount(ctx)
if err != nil {
return 0, err
}
return numBytesPerRow * numRows, nil
}
func (st StatusIgnoredTable) RowCount(_ *sql.Context) (uint64, bool, error) {
return statusIgnoredDefaultRowCount, false, nil
}
func (st StatusIgnoredTable) Name() string {
return st.tableName
}
func (st StatusIgnoredTable) String() string {
return st.tableName
}
func (st StatusIgnoredTable) Schema() sql.Schema {
return []*sql.Column{
{Name: "table_name", Type: types.Text, Source: doltdb.StatusIgnoredTableName, PrimaryKey: true, Nullable: false},
{Name: "staged", Type: types.Boolean, Source: doltdb.StatusIgnoredTableName, PrimaryKey: true, Nullable: false},
{Name: "status", Type: types.Text, Source: doltdb.StatusIgnoredTableName, PrimaryKey: true, Nullable: false},
{Name: "ignored", Type: types.Boolean, Source: doltdb.StatusIgnoredTableName, PrimaryKey: false, Nullable: false},
}
}
func (st StatusIgnoredTable) Collation() sql.CollationID {
return sql.Collation_Default
}
func (st StatusIgnoredTable) Partitions(*sql.Context) (sql.PartitionIter, error) {
return index.SinglePartitionIterFromNomsMap(nil), nil
}
func (st StatusIgnoredTable) PartitionRows(context *sql.Context, _ sql.Partition) (sql.RowIter, error) {
return newStatusIgnoredItr(context, &st)
}
// NewStatusIgnoredTable creates a new StatusIgnoredTable using either an integrators' [adapters.TableAdapter] or the
// NewStatusIgnoredTableWithNoAdapter constructor (the default implementation provided by Dolt).
func NewStatusIgnoredTable(ctx *sql.Context, tableName string, ddb *doltdb.DoltDB, ws *doltdb.WorkingSet, rp env.RootsProvider[*sql.Context]) sql.Table {
adapter, ok := adapters.DoltTableAdapterRegistry.GetAdapter(tableName)
if ok {
return adapter.NewTable(ctx, tableName, ddb, ws, rp)
}
return NewStatusIgnoredTableWithNoAdapter(ctx, tableName, ddb, ws, rp)
}
// NewStatusIgnoredTableWithNoAdapter returns a new StatusIgnoredTable.
func NewStatusIgnoredTableWithNoAdapter(_ *sql.Context, tableName string, ddb *doltdb.DoltDB, ws *doltdb.WorkingSet, rp env.RootsProvider[*sql.Context]) sql.Table {
return &StatusIgnoredTable{
tableName: tableName,
ddb: ddb,
workingSet: ws,
rootsProvider: rp,
}
}
// StatusIgnoredItr is a sql.RowIter implementation for the status_ignored table.
type StatusIgnoredItr struct {
rows []statusIgnoredTableRow
}
type statusIgnoredTableRow struct {
tableName string
status string
isStaged byte // not a bool bc wire protocol confuses bools and tinyint(1)
ignored bool
}
func newStatusIgnoredItr(ctx *sql.Context, st *StatusIgnoredTable) (*StatusIgnoredItr, error) {
// If no roots provider was set, then there is no status to report
if st.rootsProvider == nil {
return &StatusIgnoredItr{rows: nil}, nil
}
// Get the base status data using the shared function
statusRows, unstagedTables, err := getStatusRowsData(ctx, st.rootsProvider, st.workingSet)
if err != nil {
return nil, err
}
// Get ignore patterns for checking if unstaged tables are ignored
ignorePatterns, err := getIgnorePatterns(ctx, st.rootsProvider)
if err != nil {
return nil, err
}
// Build a set of unstaged table names for quick lookup
unstagedTableNames := buildUnstagedTableNameSet(unstagedTables)
// Convert status rows to status_ignored rows, adding the ignored column
rows := make([]statusIgnoredTableRow, len(statusRows))
for i, row := range statusRows {
ignored := false
// Only check ignore patterns for unstaged NEW tables (same as Git behavior).
// Tables that are modified, deleted, or renamed are already tracked,
// so ignore patterns don't apply to them.
if row.isStaged == byte(0) && row.status == newTableStatus && unstagedTableNames[row.tableName] {
tblNameObj := doltdb.TableName{Name: row.tableName}
result, err := ignorePatterns.IsTableNameIgnored(tblNameObj)
if err != nil {
return nil, err
}
if result == doltdb.Ignore {
ignored = true
}
}
rows[i] = statusIgnoredTableRow{
tableName: row.tableName,
isStaged: row.isStaged,
status: row.status,
ignored: ignored,
}
}
return &StatusIgnoredItr{rows: rows}, nil
}
// getIgnorePatterns fetches the ignore patterns from the roots provider.
func getIgnorePatterns(ctx *sql.Context, rp env.RootsProvider[*sql.Context]) (doltdb.IgnorePatterns, error) {
roots, err := rp.GetRoots(ctx)
if err != nil {
return nil, err
}
schemas := []string{doltdb.DefaultSchemaName}
ignorePatternMap, err := doltdb.GetIgnoredTablePatterns(ctx, roots, schemas)
if err != nil {
return nil, err
}
return ignorePatternMap[doltdb.DefaultSchemaName], nil
}
// buildUnstagedTableNameSet builds a set of unstaged table names for quick lookup.
func buildUnstagedTableNameSet(unstagedTables []diff.TableDelta) map[string]bool {
result := make(map[string]bool, len(unstagedTables))
for _, td := range unstagedTables {
result[td.CurName()] = true
}
return result
}
// Next retrieves the next row. It will return io.EOF if it's the last row.
func (itr *StatusIgnoredItr) Next(*sql.Context) (sql.Row, error) {
if len(itr.rows) <= 0 {
return nil, io.EOF
}
row := itr.rows[0]
itr.rows = itr.rows[1:]
return sql.NewRow(row.tableName, row.isStaged, row.status, row.ignored), nil
}
// Close closes the iterator.
func (itr *StatusIgnoredItr) Close(*sql.Context) error {
return nil
}

View File

@@ -115,6 +115,7 @@ type statusTableRow struct {
// of this table when you are using local vs remote sql connections.
}
// containsTableName checks if a table name is in the list of table names.
func containsTableName(name string, names []doltdb.TableName) bool {
for _, s := range names {
if s.String() == name {
@@ -124,21 +125,26 @@ func containsTableName(name string, names []doltdb.TableName) bool {
return false
}
func newStatusItr(ctx *sql.Context, st *StatusTable) (*StatusItr, error) {
// If no roots provider was set, then there is no status to report
rp := st.rootsProvider
// getStatusRowsData collects all status data from the given roots and working set.
// This is the shared logic used by both dolt_status and dolt_status_ignored tables.
// Returns the rows data along with the unstaged table deltas (needed for ignore checking).
func getStatusRowsData(
ctx *sql.Context,
rp env.RootsProvider[*sql.Context],
ws *doltdb.WorkingSet,
) ([]statusTableRow, []diff.TableDelta, error) {
if rp == nil {
return &StatusItr{rows: nil}, nil
return nil, nil, nil
}
roots, err := rp.GetRoots(ctx)
if err != nil {
return nil, err
return nil, nil, err
}
stagedTables, unstagedTables, err := diff.GetStagedUnstagedTableDeltas(ctx, roots)
if err != nil {
return nil, err
return nil, nil, err
}
// Some tables may differ only in column tags and/or recorded conflicts.
@@ -147,7 +153,7 @@ func newStatusItr(ctx *sql.Context, st *StatusTable) (*StatusItr, error) {
for _, unstagedTableDiff := range unstagedTables {
changed, err := unstagedTableDiff.HasChangesIgnoringColumnTags(ctx)
if err != nil {
return nil, err
return nil, nil, err
}
if changed {
changedUnstagedTables = append(changedUnstagedTables, unstagedTableDiff)
@@ -157,30 +163,30 @@ func newStatusItr(ctx *sql.Context, st *StatusTable) (*StatusItr, error) {
stagedSchemas, unstagedSchemas, err := diff.GetStagedUnstagedDatabaseSchemaDeltas(ctx, roots)
if err != nil {
return nil, err
return nil, nil, err
}
rows := make([]statusTableRow, 0, len(stagedTables)+len(unstagedTables)+len(stagedSchemas)+len(unstagedSchemas))
cvTables, err := doltdb.TablesWithConstraintViolations(ctx, roots.Working)
if err != nil {
return nil, err
return nil, nil, err
}
for _, tbl := range cvTables {
rows = append(rows, statusTableRow{
tableName: tbl.String(),
status: "constraint violation",
status: constraintViolationStatus,
})
}
if st.workingSet.MergeActive() {
ms := st.workingSet.MergeState()
if ws.MergeActive() {
ms := ws.MergeState()
for _, tbl := range ms.TablesWithSchemaConflicts() {
rows = append(rows, statusTableRow{
tableName: tbl.String(),
isStaged: byte(0),
status: "schema conflict",
status: schemaConflictStatus,
})
}
@@ -195,7 +201,7 @@ func newStatusItr(ctx *sql.Context, st *StatusTable) (*StatusItr, error) {
cnfTables, err := doltdb.TablesWithDataConflicts(ctx, roots.Working)
if err != nil {
return nil, err
return nil, nil, err
}
for _, tbl := range cnfTables {
rows = append(rows, statusTableRow{
@@ -218,6 +224,7 @@ func newStatusItr(ctx *sql.Context, st *StatusTable) (*StatusItr, error) {
status: statusString(td),
})
}
for _, td := range unstagedTables {
tblName := tableName(td)
if doltdb.IsFullTextTable(tblName) {
@@ -249,6 +256,20 @@ func newStatusItr(ctx *sql.Context, st *StatusTable) (*StatusItr, error) {
})
}
return rows, unstagedTables, nil
}
func newStatusItr(ctx *sql.Context, st *StatusTable) (*StatusItr, error) {
// If no roots provider was set, then there is no status to report
if st.rootsProvider == nil {
return &StatusItr{rows: nil}, nil
}
rows, _, err := getStatusRowsData(ctx, st.rootsProvider, st.workingSet)
if err != nil {
return nil, err
}
return &StatusItr{rows: rows}, nil
}
@@ -272,7 +293,7 @@ func tableName(td diff.TableDelta) string {
func statusString(td diff.TableDelta) string {
if td.IsAdd() {
return "new table"
return newTableStatus
} else if td.IsDrop() {
return "deleted"
} else if td.IsRename() {
@@ -284,6 +305,9 @@ func statusString(td diff.TableDelta) string {
const mergeConflictStatus = "conflict"
const mergedStatus = "merged"
const schemaConflictStatus = "schema conflict"
const constraintViolationStatus = "constraint violation"
const newTableStatus = "new table"
// Next retrieves the next row. It will return io.EOF if it's the last row.
// After retrieving the last row, Close will be automatically closed.

View File

@@ -759,6 +759,157 @@ var DoltScripts = []queries.ScriptTest{
},
},
},
{
Name: "dolt_status_ignored basic tests",
SetUpScript: []string{
"CREATE TABLE t (pk int primary key);",
"INSERT INTO dolt_ignore VALUES ('ignored_*', true);",
"CREATE TABLE ignored_test (pk int primary key);",
},
Assertions: []queries.ScriptTestAssertion{
{
// Verify schema has 4 columns
Query: "DESCRIBE dolt_status_ignored;",
Expected: []sql.Row{
{"table_name", "text", "NO", "PRI", nil, ""},
{"staged", "tinyint(1)", "NO", "PRI", nil, ""},
{"status", "text", "NO", "PRI", nil, ""},
{"ignored", "tinyint(1)", "NO", "", nil, ""},
},
},
{
// Non-ignored unstaged table has ignored=false
Query: "SELECT table_name, staged, status, ignored FROM dolt_status_ignored WHERE table_name = 't';",
Expected: []sql.Row{{"t", byte(0), "new table", false}},
},
{
// Ignored unstaged table has ignored=true
Query: "SELECT table_name, staged, status, ignored FROM dolt_status_ignored WHERE table_name = 'ignored_test';",
Expected: []sql.Row{{"ignored_test", byte(0), "new table", true}},
},
{
// dolt_ignore table itself shows as not ignored
Query: "SELECT table_name, ignored FROM dolt_status_ignored WHERE table_name = 'dolt_ignore';",
Expected: []sql.Row{{"dolt_ignore", false}},
},
},
},
{
Name: "dolt_status_ignored staged tables always have ignored=0",
SetUpScript: []string{
// Create and stage table BEFORE adding ignore pattern
"CREATE TABLE staged_test (pk int primary key);",
"CALL DOLT_ADD('staged_test');",
// Now add pattern that matches the already-staged table name
"INSERT INTO dolt_ignore VALUES ('staged_*', true);",
},
Assertions: []queries.ScriptTestAssertion{
{
// Staged table has ignored=false even if name matches ignore pattern
Query: "SELECT table_name, staged, ignored FROM dolt_status_ignored WHERE table_name = 'staged_test';",
Expected: []sql.Row{{"staged_test", byte(1), false}},
},
},
},
{
Name: "dolt_status_ignored with AS OF and branch queries",
SetUpScript: []string{
"CALL DOLT_COMMIT('--allow-empty', '-m', 'empty commit');",
"SET @commit1 = HASHOF('HEAD');",
"CALL DOLT_TAG('tag1');",
"CALL DOLT_CHECKOUT('-b', 'branch1');",
"CREATE TABLE abc (pk int);",
"CALL DOLT_ADD('abc');",
"CALL DOLT_CHECKOUT('main');",
"CREATE TABLE t (pk int primary key);",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "SELECT table_name, staged, status, ignored FROM dolt_status_ignored;",
Expected: []sql.Row{{"t", byte(0), "new table", false}},
},
{
Query: "SELECT * FROM dolt_status_ignored AS OF 'tag1';",
Expected: []sql.Row{},
},
{
Query: "SELECT * FROM dolt_status_ignored AS OF @commit1;",
Expected: []sql.Row{},
},
{
Query: "SELECT table_name, staged, status, ignored FROM dolt_status_ignored AS OF 'branch1';",
Expected: []sql.Row{{"abc", byte(1), "new table", false}},
},
{
Query: "SELECT table_name, staged, status, ignored FROM `mydb/branch1`.dolt_status_ignored;",
Expected: []sql.Row{{"abc", byte(1), "new table", false}},
},
},
},
{
Name: "dolt_status_ignored with multiple ignore patterns",
SetUpScript: []string{
"INSERT INTO dolt_ignore VALUES ('temp_*', true);",
"INSERT INTO dolt_ignore VALUES ('*_backup', true);",
"CREATE TABLE temp_data (pk int primary key);",
"CREATE TABLE users_backup (pk int primary key);",
"CREATE TABLE normal_table (pk int primary key);",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "SELECT table_name, ignored FROM dolt_status_ignored WHERE table_name = 'temp_data';",
Expected: []sql.Row{{"temp_data", true}},
},
{
Query: "SELECT table_name, ignored FROM dolt_status_ignored WHERE table_name = 'users_backup';",
Expected: []sql.Row{{"users_backup", true}},
},
{
Query: "SELECT table_name, ignored FROM dolt_status_ignored WHERE table_name = 'normal_table';",
Expected: []sql.Row{{"normal_table", false}},
},
},
},
{
Name: "dolt_status_ignored with empty dolt_ignore",
SetUpScript: []string{
"CREATE TABLE t1 (pk int primary key);",
"CREATE TABLE t2 (pk int primary key);",
},
Assertions: []queries.ScriptTestAssertion{
{
// With no ignore patterns, all tables should have ignored=false
Query: "SELECT table_name, ignored FROM dolt_status_ignored ORDER BY table_name;",
Expected: []sql.Row{
{"t1", false},
{"t2", false},
},
},
},
},
{
Name: "dolt_status_ignored with conflicting patterns",
SetUpScript: []string{
// First pattern ignores tables starting with "test_"
"INSERT INTO dolt_ignore VALUES ('test_*', true);",
// Second pattern un-ignores specific table
"INSERT INTO dolt_ignore VALUES ('test_special', false);",
"CREATE TABLE test_normal (pk int primary key);",
"CREATE TABLE test_special (pk int primary key);",
},
Assertions: []queries.ScriptTestAssertion{
{
// test_normal matches ignore pattern
Query: "SELECT table_name, ignored FROM dolt_status_ignored WHERE table_name = 'test_normal';",
Expected: []sql.Row{{"test_normal", true}},
},
{
// test_special is explicitly not ignored (false overrides wildcard)
Query: "SELECT table_name, ignored FROM dolt_status_ignored WHERE table_name = 'test_special';",
Expected: []sql.Row{{"test_special", false}},
},
},
},
{
Name: "dolt_hashof_table tests",
SetUpScript: []string{
@@ -8670,6 +8821,7 @@ var DoltSystemVariables = []queries.ScriptTest{
{"dolt_remotes"},
{"dolt_stashes"},
{"dolt_status"},
{"dolt_status_ignored"},
{"dolt_workspace_test"},
{"test"},
},

View File

@@ -60,9 +60,10 @@ teardown() {
@test "ls: --system shows system tables" {
run dolt ls --system
[ "$status" -eq 0 ]
[ "${#lines[@]}" -eq 26 ]
[ "${#lines[@]}" -eq 27 ]
[[ "$output" =~ "System tables:" ]] || false
[[ "$output" =~ "dolt_status" ]] || false
[[ "$output" =~ "dolt_status_ignored" ]] || false
[[ "$output" =~ "dolt_commits" ]] || false
[[ "$output" =~ "dolt_commit_ancestors" ]] || false
[[ "$output" =~ "dolt_constraint_violations" ]] || false

View File

@@ -1165,3 +1165,70 @@ SQL
[ "$status" -eq 0 ]
[[ "$output" =~ "dolt_conflict_id" ]] || false
}
@test "system-tables: query dolt_status_ignored system table" {
# Create a table and add it
dolt sql -q "CREATE TABLE test (pk INT PRIMARY KEY, c1 INT)"
dolt add test
dolt commit -m "Added test table"
# Create an ignored table pattern
dolt sql -q "INSERT INTO dolt_ignore VALUES ('ignored_*', true)"
# Create a table that matches the ignore pattern
dolt sql -q "CREATE TABLE ignored_table (pk INT PRIMARY KEY)"
# Create a non-ignored table with changes
dolt sql -q "INSERT INTO test VALUES (1, 1)"
# Query dolt_status - shows all tables (does not filter ignored)
run dolt sql -q "SELECT table_name FROM dolt_status WHERE staged = false"
[ "$status" -eq 0 ]
[[ "$output" =~ "test" ]] || false
[[ "$output" =~ "ignored_table" ]] || false
# Query dolt_status_ignored - shows all tables with ignored column
run dolt sql -q "SELECT table_name, ignored FROM dolt_status_ignored WHERE staged = false ORDER BY table_name"
[ "$status" -eq 0 ]
[[ "$output" =~ "ignored_table" ]] || false
[[ "$output" =~ "test" ]] || false
# Verify ignored column correctly identifies ignored tables
run dolt sql -r csv -q "SELECT table_name, ignored FROM dolt_status_ignored WHERE table_name = 'ignored_table'"
[ "$status" -eq 0 ]
[[ "$output" =~ "ignored_table,true" ]] || false
# Verify non-ignored table has ignored = false
run dolt sql -r csv -q "SELECT table_name, ignored FROM dolt_status_ignored WHERE table_name = 'test'"
[ "$status" -eq 0 ]
[[ "$output" =~ "test,false" ]] || false
# Verify schema has 4 columns
run dolt sql -q "DESCRIBE dolt_status_ignored"
[ "$status" -eq 0 ]
[[ "$output" =~ "table_name" ]] || false
[[ "$output" =~ "staged" ]] || false
[[ "$output" =~ "status" ]] || false
[[ "$output" =~ "ignored" ]] || false
}
@test "system-tables: dolt_status_ignored shows staged tables without ignored flag" {
# Create and stage a table
dolt sql -q "CREATE TABLE staged_test (pk INT PRIMARY KEY)"
dolt add staged_test
# Query staged tables - should have ignored = false
run dolt sql -q "SELECT table_name, ignored FROM dolt_status_ignored WHERE staged = true AND table_name = 'staged_test'"
[ "$status" -eq 0 ]
[[ "$output" =~ "staged_test" ]] || false
# Staged tables should never be marked as ignored
run dolt sql -r csv -q "SELECT ignored FROM dolt_status_ignored WHERE staged = true AND table_name = 'staged_test'"
[ "$status" -eq 0 ]
[[ "$output" =~ "false" ]] || false
}
@test "system-tables: dolt_status_ignored shows in dolt ls --system" {
run dolt ls --system
[ "$status" -eq 0 ]
[[ "$output" =~ "dolt_status_ignored" ]] || false
}