Files
wehub-resource-sync 5357c39144
Fuzzer / Run Fuzzer (push) Has been cancelled
Race tests / Go race tests (ubuntu-22.04) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:01:40 +08:00

149 lines
4.7 KiB
Go

// 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 prompt
import (
"errors"
"strings"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
)
// Parts contains shell prompt components to render the SQL shell prompt.
type Parts struct {
BaseDatabase string
ActiveRevision string
RevisionDelimiter string
IsBranch bool
Dirty bool
}
// Resolver resolves prompt [prompt.Parts] for the active session.
type Resolver interface {
Resolve(sqlCtx *sql.Context, queryist cli.Queryist) (parts Parts, resolved bool, err error)
}
// sqlDBActiveBranchResolver can resolve [prompt.Parts] using the Dolt SQL functions, and supports
// [doltdb.DbRevisionDelimiter] and [doltdb.DbRevisionDelimiterAlias].
type sqlDBActiveBranchResolver struct{}
// chainedResolver can resolve [prompt.Parts] through the sequential execution [prompt.Resolver](s).
type chainedResolver struct {
resolvers []Resolver
}
// NewPromptResolver constructs an up-to-date [prompt.Resolver].
func NewPromptResolver() Resolver {
return chainedResolver{
resolvers: []Resolver{
sqlDBActiveBranchResolver{},
},
}
}
// Resolve resolves [prompt.Parts] through a chain of [prompt.Resolver](s) in sequential order. If a Resolver encounters
// an error, it is returned immediately and no other Resolver executes.
func (cr chainedResolver) Resolve(sqlCtx *sql.Context, queryist cli.Queryist) (parts Parts, resolved bool, err error) {
for _, resolver := range cr.resolvers {
parts, resolved, err := resolver.Resolve(sqlCtx, queryist)
if err != nil {
return Parts{}, false, err
}
if resolved {
return parts, true, nil
}
}
return Parts{}, false, nil
}
// Resolve resolves the base DB and revision through the SQL function `database()` and Dolt-specific `active_branch()`.
func (sqlDBActiveBranchResolver) Resolve(sqlCtx *sql.Context, queryist cli.Queryist) (parts Parts, resolved bool, err error) {
dbRows, err := cli.GetRowsForSql(queryist, sqlCtx, "select database() as db")
if err != nil {
return parts, false, err
}
if len(dbRows) > 0 && len(dbRows[0]) > 0 {
dbName, err := cli.QueryValueAsString(dbRows[0][0])
if err != nil {
return parts, false, err
}
// Handles non-branch revisions (i.e., commit hash, tags, etc.).
parts.BaseDatabase, parts.ActiveRevision = doltdb.SplitRevisionDbName(dbName)
parts.RevisionDelimiter = doltdb.DbRevisionDelimiter
for _, delimiter := range doltdb.DBRevisionDelimiters {
if strings.Contains(dbName, delimiter) {
parts.RevisionDelimiter = delimiter
break
}
}
}
activeBranchRows, err := cli.GetRowsForSql(queryist, sqlCtx, "select active_branch() as branch")
if err != nil {
return parts, false, err
}
if len(activeBranchRows) > 0 && len(activeBranchRows[0]) > 0 {
parts.IsBranch = activeBranchRows[0][0] != nil
if parts.ActiveRevision == "" {
parts.ActiveRevision, err = cli.QueryValueAsString(activeBranchRows[0][0])
if err != nil {
return parts, false, err
}
}
}
parts.Dirty, err = resolveDirty(sqlCtx, queryist, parts)
if err != nil {
return parts, false, err
}
return parts, true, nil
}
// resolveDirty resolves the dirty state of the current branch and whether the revision type is a branch.
func resolveDirty(sqlCtx *sql.Context, queryist cli.Queryist, parts Parts) (dirty bool, err error) {
if parts.BaseDatabase == "" {
return false, nil
}
if doltdb.IsValidCommitHash(parts.ActiveRevision) {
return false, nil
}
rows, err := cli.GetRowsForSql(queryist, sqlCtx, "select count(table_name) > 0 as dirty from dolt_status")
// [sql.ErrTableNotFound] detects when viewing a non-Dolt database (e.g., information_schema). Older servers may
// complain about [doltdb.ErrOperationNotSupportedInDetachedHead], but read-only revisions in newer versions this
// issue should be gone.
if errors.Is(err, doltdb.ErrOperationNotSupportedInDetachedHead) || sql.ErrTableNotFound.Is(err) {
return false, nil
} else if err != nil {
return false, err
}
if len(rows) == 0 || len(rows[0]) == 0 {
return false, nil
}
dirty, err = cli.QueryValueAsBool(rows[0][0])
if err != nil {
return false, err
}
return dirty, nil
}