mirror of
https://github.com/dolthub/dolt.git
synced 2026-03-14 19:20:44 -05:00
amend sql-shell resolver to evaluate using active_branch() and database() only and make @ delimiter unusable in normal database names
This commit is contained in:
@@ -16,6 +16,7 @@ package prompt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
@@ -23,26 +24,25 @@ import (
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
)
|
||||
|
||||
// Parts contains shell prompt components to render in the final prompt.
|
||||
// Parts contains shell prompt components to render the SQL shell prompt.
|
||||
type Parts struct {
|
||||
BaseDatabase string
|
||||
ActiveRevision string
|
||||
IsBranch bool
|
||||
Dirty bool
|
||||
BaseDatabase string
|
||||
ActiveRevision string
|
||||
RevisionDelimiter string
|
||||
IsBranch bool
|
||||
Dirty bool
|
||||
}
|
||||
|
||||
// Resolver resolves prompt Parts for the active session.
|
||||
// revisionDelimiters follows [doltdb]'s definition to separate the base database from the revision.
|
||||
var revisionDelimiters = []string{doltdb.DbRevisionDelimiter, doltdb.DbRevisionDelimiterAlias}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// sqlBaseRevisionResolver can resolve [prompt.Parts] using Dolt specific SQL functions that return canonical base
|
||||
// database and revision, even when the [doltdb.DbRevisionDelimiterAlias] is in use.
|
||||
type sqlBaseRevisionResolver struct{}
|
||||
|
||||
// sqlDBActiveBranchResolver can resolve [prompt.Parts] using the SQL-specific functions. It is a fallback for older
|
||||
// servers, and is the method older shells use in general. This resolver does not support
|
||||
// [doltdb.DbRevisionDelimiterAlias] as a revision delimiter.
|
||||
// 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).
|
||||
@@ -50,11 +50,10 @@ type chainedResolver struct {
|
||||
resolvers []Resolver
|
||||
}
|
||||
|
||||
// NewPartsResolver constructs an up-to-date [prompt.Resolver].
|
||||
func NewPartsResolver() Resolver {
|
||||
// NewPromptResolver constructs an up-to-date [prompt.Resolver].
|
||||
func NewPromptResolver() Resolver {
|
||||
return chainedResolver{
|
||||
resolvers: []Resolver{
|
||||
sqlBaseRevisionResolver{},
|
||||
sqlDBActiveBranchResolver{},
|
||||
},
|
||||
}
|
||||
@@ -75,97 +74,75 @@ func (cr chainedResolver) Resolve(sqlCtx *sql.Context, queryist cli.Queryist) (p
|
||||
return Parts{}, false, nil
|
||||
}
|
||||
|
||||
// Resolve resolves [prompt.Parts] through SQL functions `base_database()` and `active_revision()`.
|
||||
func (sqlBaseRevisionResolver) Resolve(sqlCtx *sql.Context, queryist cli.Queryist) (parts Parts, resolved bool, err error) {
|
||||
parts = Parts{}
|
||||
|
||||
rows, err := cli.GetRowsForSql(queryist, sqlCtx, "select base_database() as base_database, active_revision() as active_revision")
|
||||
if sql.ErrFunctionNotFound.Is(err) {
|
||||
// Running on an older version.
|
||||
return parts, false, nil
|
||||
} else if err != nil {
|
||||
return parts, false, err
|
||||
}
|
||||
|
||||
if len(rows) > 0 {
|
||||
if len(rows[0]) > 0 {
|
||||
parts.BaseDatabase, err = cli.GetStringColumnValue(rows[0][0])
|
||||
if err != nil {
|
||||
return parts, false, err
|
||||
}
|
||||
}
|
||||
if len(rows[0]) > 1 {
|
||||
parts.ActiveRevision, err = cli.GetStringColumnValue(rows[0][1])
|
||||
if err != nil {
|
||||
return parts, false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parts.Dirty, parts.IsBranch, err = resolveDirty(sqlCtx, queryist, parts)
|
||||
if err != nil {
|
||||
return parts, false, err
|
||||
}
|
||||
return parts, true, nil
|
||||
}
|
||||
|
||||
// Resolve resolves the base database and active revision through the SQL-specific functions `database()` and
|
||||
// `active_branch()`. Unfortunately, to maintain support for ORMs that rely on the database in their connection URL,
|
||||
// this method cannot interpret [doltdb.DbRevisionDelimiterAlias] as a revision delimiter.
|
||||
// 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) {
|
||||
parts = Parts{}
|
||||
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.GetStringColumnValue(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 revisionDelimiters {
|
||||
if strings.Contains(dbName, delimiter) {
|
||||
parts.RevisionDelimiter = delimiter
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if parts.ActiveRevision == "" {
|
||||
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.ActiveRevision, err = cli.GetStringColumnValue(activeBranchRows[0][0])
|
||||
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, parts.IsBranch, err = resolveDirty(sqlCtx, queryist, parts)
|
||||
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, isBranch bool, err error) {
|
||||
func resolveDirty(sqlCtx *sql.Context, queryist cli.Queryist, parts Parts) (dirty bool, err error) {
|
||||
if doltdb.IsValidCommitHash(parts.ActiveRevision) {
|
||||
return false, false, nil
|
||||
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, false, nil
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
return false, false, err
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(rows) == 0 || len(rows[0]) == 0 {
|
||||
return false, false, nil
|
||||
return false, nil
|
||||
}
|
||||
dirty, err = cli.GetBoolColumnValue(rows[0][0])
|
||||
dirty, err = cli.QueryValueAsBool(rows[0][0])
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
return false, err
|
||||
}
|
||||
|
||||
return dirty, true, nil
|
||||
return dirty, nil
|
||||
}
|
||||
|
||||
@@ -21,26 +21,6 @@ import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
)
|
||||
|
||||
// GetInt8ColAsBool returns the value of an int8 column as a bool
|
||||
// This is necessary because Queryist may return an int8 column as a bool (when using SQLEngine)
|
||||
// or as a string (when using ConnectionQueryist).
|
||||
func GetInt8ColAsBool(col interface{}) (bool, error) {
|
||||
switch v := col.(type) {
|
||||
case int8:
|
||||
return v != 0, nil
|
||||
case string:
|
||||
if v == "ON" || v == "1" {
|
||||
return true, nil
|
||||
} else if v == "OFF" || v == "0" {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, fmt.Errorf("unexpected value for boolean var: %v", v)
|
||||
}
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected type %T, was expecting int8", v)
|
||||
}
|
||||
}
|
||||
|
||||
// SetSystemVar sets the @@dolt_show_system_tables variable if necessary, and returns a function
|
||||
// resetting the variable for after the commands completion, if necessary.
|
||||
func SetSystemVar(queryist Queryist, sqlCtx *sql.Context, newVal bool) (func() error, error) {
|
||||
@@ -53,7 +33,7 @@ func SetSystemVar(queryist Queryist, sqlCtx *sql.Context, newVal bool) (func() e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prevVal, err := GetInt8ColAsBool(row[0][1])
|
||||
prevVal, err := QueryValueAsBool(row[0][1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -85,8 +65,10 @@ func GetRowsForSql(queryist Queryist, sqlCtx *sql.Context, query string) ([]sql.
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
// GetStringColumnValue returns column values from [sql.Row] as a string.
|
||||
func GetStringColumnValue(value any) (str string, err error) {
|
||||
// QueryValueAsString converts a single value from a query result to a string. Use this when reading string-like
|
||||
// columns from Queryist results, since the type can differ in-process [engine.SQLEngine] versus over the wire
|
||||
// [sqlserver.ConnectionQueryist].
|
||||
func QueryValueAsString(value any) (str string, err error) {
|
||||
if value == nil {
|
||||
return "", nil
|
||||
}
|
||||
@@ -103,16 +85,31 @@ func GetStringColumnValue(value any) (str string, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetBoolColumnValue returns the value of the input as a bool. This is required because depending on if we go over the
|
||||
// wire or not we may get a string or a bool when we expect a bool.
|
||||
func GetBoolColumnValue(col interface{}) (bool, error) {
|
||||
// QueryValueAsBool interprets a query result cell as a bool. Strings are normalized and matched as "true"/"1"/"ON"
|
||||
// (true) or the opposite for false; matching is case-insensitive. [Queryist] may return a tinyint column as a bool
|
||||
// when utilizing the [engine.SQLEngine] or as string when using [sqlserver.ConnectionQueryist].
|
||||
func QueryValueAsBool(col interface{}) (bool, error) {
|
||||
switch v := col.(type) {
|
||||
case bool:
|
||||
return col.(bool), nil
|
||||
return v, nil
|
||||
case byte:
|
||||
return v == 1, nil
|
||||
case int:
|
||||
return v == 1, nil
|
||||
case int8:
|
||||
return v == 1, nil
|
||||
case string:
|
||||
return strings.EqualFold(col.(string), "true") || strings.EqualFold(col.(string), "1"), nil
|
||||
s := strings.TrimSpace(v)
|
||||
switch {
|
||||
case s == "1" || strings.EqualFold(s, "true") || strings.EqualFold(s, "ON"):
|
||||
return true, nil
|
||||
case s == "0" || strings.EqualFold(s, "false") || strings.EqualFold(s, "OFF"):
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected value for string for bool: %v", v)
|
||||
}
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected type %T, was expecting bool or string", v)
|
||||
return false, fmt.Errorf("unexpected type %T, was expecting bool, int, or string", v)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -305,7 +305,7 @@ func getMergeStatus(queryist cli.Queryist, sqlCtx *sql.Context) (mergeStatus, er
|
||||
}
|
||||
|
||||
row := rows[0]
|
||||
ms.isMerging, err = commands.GetTinyIntColAsBool(row[0])
|
||||
ms.isMerging, err = cli.QueryValueAsBool(row[0])
|
||||
if err != nil {
|
||||
return ms, fmt.Errorf("error: failed to parse is_merging: %w", err)
|
||||
}
|
||||
|
||||
@@ -874,11 +874,11 @@ func getDiffSummariesBetweenRefs(queryist cli.Queryist, sqlCtx *sql.Context, fro
|
||||
summary.FromTableName.Name = row[0].(string)
|
||||
summary.ToTableName.Name = row[1].(string)
|
||||
summary.DiffType = row[2].(string)
|
||||
summary.DataChange, err = GetTinyIntColAsBool(row[3])
|
||||
summary.DataChange, err = cli.QueryValueAsBool(row[3])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error: unable to parse data change value '%s': %w", row[3], err)
|
||||
}
|
||||
summary.SchemaChange, err = GetTinyIntColAsBool(row[4])
|
||||
summary.SchemaChange, err = cli.QueryValueAsBool(row[4])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error: unable to parse schema change value '%s': %w", row[4], err)
|
||||
}
|
||||
|
||||
@@ -952,28 +952,32 @@ func preprocessQuery(query, lastQuery string, cliCtx cli.CliContext) (CommandTyp
|
||||
// postCommandUpdate is a helper function that is run after the shell has completed a command. It updates the database
|
||||
// if needed, and generates new prompts for the shell (based on the branch and if the workspace is dirty).
|
||||
func postCommandUpdate(sqlCtx *sql.Context, qryist cli.Queryist) (string, string) {
|
||||
resolver := prompt.NewPartsResolver()
|
||||
promptResolver := prompt.NewPromptResolver()
|
||||
var parts prompt.Parts
|
||||
var resolved bool
|
||||
|
||||
resolved := false
|
||||
err := cli.WithQueryWarningsLocked(sqlCtx, qryist, func() error {
|
||||
var err error
|
||||
parts, resolved, err = resolver.Resolve(sqlCtx, qryist)
|
||||
parts, resolved, err = promptResolver.Resolve(sqlCtx, qryist)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
cli.PrintErrln(err.Error())
|
||||
}
|
||||
if resolved && parts.ActiveRevision != "" {
|
||||
sqlCtx.SetCurrentDatabase(parts.BaseDatabase + doltdb.DbRevisionDelimiter + parts.ActiveRevision)
|
||||
} else if resolved {
|
||||
sqlCtx.SetCurrentDatabase(parts.BaseDatabase)
|
||||
} else {
|
||||
cli.PrintErrln(color.YellowString("Failed to set new current database for the post command update"))
|
||||
baseDatabase, activeRevision := doltdb.SplitRevisionDbName(sqlCtx.GetCurrentDatabase())
|
||||
return formattedPrompts(baseDatabase, activeRevision, false)
|
||||
|
||||
if !resolved {
|
||||
cli.PrintErrln(color.YellowString("Failed to set new current database on post command update"))
|
||||
parts.BaseDatabase, parts.ActiveRevision = doltdb.SplitRevisionDbName(sqlCtx.GetCurrentDatabase())
|
||||
return formattedPrompts(parts.BaseDatabase, parts.ActiveRevision, parts.Dirty)
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
builder.WriteString(parts.BaseDatabase)
|
||||
if parts.ActiveRevision != "" {
|
||||
builder.WriteString(parts.RevisionDelimiter)
|
||||
builder.WriteString(parts.ActiveRevision)
|
||||
}
|
||||
sqlCtx.SetCurrentDatabase(builder.String())
|
||||
|
||||
return formattedPrompts(parts.BaseDatabase, parts.ActiveRevision, parts.Dirty)
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ Found a bug? Want additional features? Please let us know! https://github.com/do
|
||||
}
|
||||
|
||||
func generateHelpPrompt(sqlCtx *sql.Context, qryist cli.Queryist) string {
|
||||
resolver := prompt.NewPartsResolver()
|
||||
resolver := prompt.NewPromptResolver()
|
||||
var parts prompt.Parts
|
||||
var resolved bool
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ func createPrintData(queryist cli.Queryist, sqlCtx *sql.Context, showIgnoredTabl
|
||||
staged := row[1]
|
||||
status := row[2].(string)
|
||||
|
||||
isStaged, err := GetTinyIntColAsBool(staged)
|
||||
isStaged, err := cli.QueryValueAsBool(staged)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -389,7 +389,7 @@ func getMergeStatus(queryist cli.Queryist, sqlCtx *sql.Context) (bool, error) {
|
||||
mergeActive := false
|
||||
if len(mergeRows) == 1 {
|
||||
isMerging := mergeRows[0][0]
|
||||
mergeActive, err = GetTinyIntColAsBool(isMerging)
|
||||
mergeActive, err = cli.QueryValueAsBool(isMerging)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -354,24 +354,6 @@ func InterpolateAndRunQuery(queryist cli.Queryist, sqlCtx *sql.Context, queryTem
|
||||
return cli.GetRowsForSql(queryist, sqlCtx, query)
|
||||
}
|
||||
|
||||
// GetTinyIntColAsBool returns the value of a tinyint column as a bool
|
||||
// This is necessary because Queryist may return a tinyint column as a bool (when using SQLEngine)
|
||||
// or as a string (when using ConnectionQueryist).
|
||||
func GetTinyIntColAsBool(col interface{}) (bool, error) {
|
||||
switch v := col.(type) {
|
||||
case bool:
|
||||
return v, nil
|
||||
case byte:
|
||||
return v == 1, nil
|
||||
case int:
|
||||
return v == 1, nil
|
||||
case string:
|
||||
return v == "1", nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected type %T, was expecting bool, int, or string", v)
|
||||
}
|
||||
}
|
||||
|
||||
// getInt64ColAsInt64 returns the value of an int64 column as an int64
|
||||
// This is necessary because Queryist may return an int64 column as an int64 (when using SQLEngine)
|
||||
// or as a string (when using ConnectionQueryist).
|
||||
@@ -562,7 +544,7 @@ func GetDoltStatus(queryist cli.Queryist, sqlCtx *sql.Context) (stagedChangedTab
|
||||
tableName := row[0].(string)
|
||||
staged := row[1]
|
||||
var isStaged bool
|
||||
isStaged, err = GetTinyIntColAsBool(staged)
|
||||
isStaged, err = cli.QueryValueAsBool(staged)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ require (
|
||||
github.com/dolthub/fslock v0.0.0-20251215194149-ef20baba2318
|
||||
github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718
|
||||
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81
|
||||
github.com/dolthub/vitess v0.0.0-20260202234501-b14ed9b1632b
|
||||
github.com/dolthub/vitess v0.0.0-20260225173707-20566e4abe9e
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568
|
||||
@@ -61,7 +61,7 @@ require (
|
||||
github.com/dolthub/dolt-mcp v0.2.2
|
||||
github.com/dolthub/eventsapi_schema v0.0.0-20260205214132-a7a3c84c84a1
|
||||
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2
|
||||
github.com/dolthub/go-mysql-server v0.20.1-0.20260206233720-bbef18042f77
|
||||
github.com/dolthub/go-mysql-server v0.20.1-0.20260225184209-84b75b1e95bb
|
||||
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63
|
||||
github.com/edsrzf/mmap-go v1.2.0
|
||||
github.com/esote/minmaxheap v1.0.0
|
||||
|
||||
@@ -196,8 +196,8 @@ github.com/dolthub/fslock v0.0.0-20251215194149-ef20baba2318 h1:n+vdH5G5Db+1qnDC
|
||||
github.com/dolthub/fslock v0.0.0-20251215194149-ef20baba2318/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0=
|
||||
github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790 h1:zxMsH7RLiG+dlZ/y0LgJHTV26XoiSJcuWq+em6t6VVc=
|
||||
github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790/go.mod h1:F3cnm+vMRK1HaU6+rNqQrOCyR03HHhR1GWG2gnPOqaE=
|
||||
github.com/dolthub/go-mysql-server v0.20.1-0.20260206233720-bbef18042f77 h1:1b6Z3rm58d5LtLFQI2olPwnNTbwC1g7aTVRhrO6HJdc=
|
||||
github.com/dolthub/go-mysql-server v0.20.1-0.20260206233720-bbef18042f77/go.mod h1:LEWdXw6LKjdonOv2X808RpUc8wZVtQx4ZEPvmDWkvY4=
|
||||
github.com/dolthub/go-mysql-server v0.20.1-0.20260225184209-84b75b1e95bb h1:G1XtL3WMaCz11uHDFoQVqgQdkMptTQ/wQYJiWXCv5kk=
|
||||
github.com/dolthub/go-mysql-server v0.20.1-0.20260225184209-84b75b1e95bb/go.mod h1:Ip8uuT18T+T6kXiRHLluThFBiJZsgbJFsFp3VhdlT4Q=
|
||||
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI=
|
||||
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q=
|
||||
github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE=
|
||||
@@ -206,8 +206,8 @@ github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 h1:bMGS25NWAGTE
|
||||
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71/go.mod h1:2/2zjLQ/JOOSbbSboojeg+cAwcRV0fDLzIiWch/lhqI=
|
||||
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 h1:7/v8q9XGFa6q5Ap4Z/OhNkAMBaK5YeuEzwJt+NZdhiE=
|
||||
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY=
|
||||
github.com/dolthub/vitess v0.0.0-20260202234501-b14ed9b1632b h1:B8QS0U5EHtJTiOptjti1cH/OiE6uczyhePtvVFigf3w=
|
||||
github.com/dolthub/vitess v0.0.0-20260202234501-b14ed9b1632b/go.mod h1:eLLslh1CSPMf89pPcaMG4yM72PQbTN9OUYJeAy0fAis=
|
||||
github.com/dolthub/vitess v0.0.0-20260225173707-20566e4abe9e h1:ZrRCF8F8Iq8RP0OBowkYpOwd/1NTFU34Ydp0MZ2qTq4=
|
||||
github.com/dolthub/vitess v0.0.0-20260225173707-20566e4abe9e/go.mod h1:eLLslh1CSPMf89pPcaMG4yM72PQbTN9OUYJeAy0fAis=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
|
||||
@@ -2394,28 +2394,13 @@ func RevisionDbName(baseName string, rev string) string {
|
||||
return baseName + DbRevisionDelimiter + rev
|
||||
}
|
||||
|
||||
// SplitRevisionDbName returns the base database name and revision from a traditional revision-qualified name. Splits on
|
||||
// the first "/".
|
||||
// SplitRevisionDbName returns the base database name and revision from a revision-qualified name. Resolves on the
|
||||
// [DbRevisionDelimiter] and [DbRevisionDelimiterAlias].
|
||||
func SplitRevisionDbName(dbName string) (string, string) {
|
||||
if idx := strings.Index(dbName, DbRevisionDelimiter); idx >= 0 {
|
||||
return dbName[:idx], dbName[idx+1:]
|
||||
if base, revision, ok := strings.Cut(dbName, DbRevisionDelimiter); ok && base != "" {
|
||||
return base, revision
|
||||
} else if base, revision, ok := strings.Cut(dbName, DbRevisionDelimiterAlias); ok && base != "" {
|
||||
return base, revision
|
||||
}
|
||||
return dbName, ""
|
||||
}
|
||||
|
||||
// NormalizeRevisionDelimiter rewrites "base@revision" names to "base/revision". Names that already contain "/" are
|
||||
// returned unchanged so bases that include "@" keep their existing interpretation.
|
||||
func NormalizeRevisionDelimiter(dbName string) (rewrite string, usesDelimiterAlias bool) {
|
||||
if strings.Contains(dbName, DbRevisionDelimiter) {
|
||||
return dbName, false
|
||||
}
|
||||
|
||||
lastAliasIndex := strings.LastIndex(dbName, DbRevisionDelimiterAlias)
|
||||
if lastAliasIndex < 0 {
|
||||
return dbName, false
|
||||
}
|
||||
|
||||
base := dbName[:lastAliasIndex]
|
||||
revision := dbName[lastAliasIndex+1:]
|
||||
return RevisionDbName(base, revision), true
|
||||
}
|
||||
|
||||
@@ -1279,6 +1279,11 @@ func getStatusTableRootsProvider(
|
||||
concurrentmap.New[string, env.Remote]())
|
||||
ws, err := sess.WorkingSet(ctx, db.RevisionQualifiedName())
|
||||
if err != nil {
|
||||
// Detached HEAD databases are read-only references and do not have a working set.
|
||||
// Status tables should treat them as clean and return no rows.
|
||||
if err == doltdb.ErrOperationNotSupportedInDetachedHead {
|
||||
return nil, nil, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -418,34 +418,20 @@ func (p *DoltDatabaseProvider) HasDatabase(ctx *sql.Context, name string) bool {
|
||||
}
|
||||
|
||||
func (p *DoltDatabaseProvider) AllDatabases(ctx *sql.Context) (all []sql.Database) {
|
||||
normalized, usesDelimiterAlias := doltdb.NormalizeRevisionDelimiter(ctx.GetCurrentDatabase())
|
||||
baseName, currentRevision := doltdb.SplitRevisionDbName(normalized)
|
||||
|
||||
_, revision := doltdb.SplitRevisionDbName(ctx.GetCurrentDatabase())
|
||||
p.mu.RLock()
|
||||
|
||||
showBranches, err := dsess.GetBooleanSystemVar(ctx, dsess.ShowBranchDatabases)
|
||||
if err != nil {
|
||||
ctx.GetLogger().Warn(err)
|
||||
}
|
||||
|
||||
rdb, ok, err := p.databaseForRevision(ctx, normalized, ctx.GetCurrentDatabase())
|
||||
skipConflictDBName := ""
|
||||
if usesDelimiterAlias && baseName != ctx.GetCurrentDatabase() {
|
||||
conflictDB, conflictDBOk := p.databases[ctx.GetCurrentDatabase()]
|
||||
if conflictDBOk && ok && err == nil {
|
||||
skipConflictDBName = conflictDB.AliasedName()
|
||||
}
|
||||
}
|
||||
|
||||
all = make([]sql.Database, 0, len(p.databases))
|
||||
for _, db := range p.databases {
|
||||
if skipConflictDBName == db.AliasedName() {
|
||||
continue
|
||||
}
|
||||
|
||||
all = append(all, db)
|
||||
|
||||
if showBranches && db.Name() != clusterdb.DoltClusterDbName {
|
||||
revisionDbs, err := p.allRevisionDbs(ctx, db, normalized)
|
||||
revisionDbs, err := p.allRevisionDbs(ctx, db, formatDbMapKeyName(ctx.GetCurrentDatabase()))
|
||||
if err != nil {
|
||||
// TODO: this interface is wrong, needs to return errors
|
||||
ctx.GetLogger().Warnf("error fetching revision databases: %s", err.Error())
|
||||
@@ -456,15 +442,12 @@ func (p *DoltDatabaseProvider) AllDatabases(ctx *sql.Context) (all []sql.Databas
|
||||
}
|
||||
p.mu.RUnlock()
|
||||
|
||||
// If there's a revision database in use, include it in the list (but don't double-count). When showBranches is off
|
||||
// we still include the current revision db if one is in use, so the active database is always visible in SHOW
|
||||
// DATABASES.
|
||||
if currentRevision != "" && !showBranches {
|
||||
// If there's a revision database in use, include it in the list (but don't double-count).
|
||||
if revision != "" && !showBranches {
|
||||
rdb, ok, err := p.databaseForRevision(ctx, formatDbMapKeyName(ctx.GetCurrentDatabase()), ctx.GetCurrentDatabase())
|
||||
if err != nil {
|
||||
// TODO: this interface is wrong, needs to return errors
|
||||
if !sql.ErrDatabaseNotFound.Is(err) {
|
||||
ctx.GetLogger().Warnf("error fetching revision databases: %s", err.Error())
|
||||
}
|
||||
ctx.GetLogger().Warnf("error fetching revision databases: %s", err.Error())
|
||||
} else if ok {
|
||||
all = append(all, rdb)
|
||||
}
|
||||
@@ -498,7 +481,7 @@ func (p *DoltDatabaseProvider) DoltDatabases() []dsess.SqlDatabase {
|
||||
}
|
||||
|
||||
// allRevisionDbs returns all revision dbs for the database given
|
||||
func (p *DoltDatabaseProvider) allRevisionDbs(ctx *sql.Context, db dsess.SqlDatabase, currDb string) ([]sql.Database, error) {
|
||||
func (p *DoltDatabaseProvider) allRevisionDbs(ctx *sql.Context, db dsess.SqlDatabase, currentDB string) ([]sql.Database, error) {
|
||||
branches, err := db.DbData().Ddb.GetBranches(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -510,7 +493,7 @@ func (p *DoltDatabaseProvider) allRevisionDbs(ctx *sql.Context, db dsess.SqlData
|
||||
requestedName := revisionQualifiedName
|
||||
// If the current DB matches, it means we're either using `@` or `/` delimited revision database name. So, we
|
||||
// replace the revisionQualifiedName with the [ctx.GetCurrentDatabase] result to maintain the exact delimiter.
|
||||
if revisionQualifiedName == currDb {
|
||||
if revisionQualifiedName == currentDB {
|
||||
requestedName = ctx.GetCurrentDatabase()
|
||||
}
|
||||
revDb, ok, err := p.databaseForRevision(ctx, revisionQualifiedName, requestedName)
|
||||
@@ -561,6 +544,11 @@ func commitTransaction(ctx *sql.Context, dSess *dsess.DoltSession, rsc *doltdb.R
|
||||
}
|
||||
|
||||
func (p *DoltDatabaseProvider) CreateCollatedDatabase(ctx *sql.Context, name string, collation sql.CollationID) (err error) {
|
||||
// On Windows we have to check before creating the directory to avoid a process lock.
|
||||
if strings.ContainsAny(name, doltdb.DbRevisionDelimiterAlias+doltdb.DbRevisionDelimiter) {
|
||||
return sql.ErrWrongDBName.New(name)
|
||||
}
|
||||
|
||||
exists, isDir := p.fs.Exists(name)
|
||||
if exists && isDir {
|
||||
return sql.ErrDatabaseExists.New(name)
|
||||
@@ -865,10 +853,7 @@ func (p *DoltDatabaseProvider) cloneDatabaseFromRemote(
|
||||
// DropDatabase implements the sql.MutableDatabaseProvider interface
|
||||
func (p *DoltDatabaseProvider) DropDatabase(ctx *sql.Context, name string) error {
|
||||
_, revision := doltdb.SplitRevisionDbName(name)
|
||||
normalized, usesDelimiterAlias := doltdb.NormalizeRevisionDelimiter(name)
|
||||
// To maintain parity with CreateDatabase we do the same call as GMS with HasDatabase, if a revision exists using
|
||||
// the `@` delimiter an error will occur. Otherwise, no error.
|
||||
if revision != "" || (usesDelimiterAlias && p.HasDatabase(ctx, normalized)) {
|
||||
if revision != "" {
|
||||
return fmt.Errorf("unable to drop revision database: %s", name)
|
||||
}
|
||||
|
||||
@@ -977,6 +962,12 @@ func (p *DoltDatabaseProvider) PurgeDroppedDatabases(ctx *sql.Context) error {
|
||||
// function is responsible for instantiating the new Database instance and updating the tracking metadata
|
||||
// in this provider. If any problems are encountered while registering the new database, an error is returned.
|
||||
func (p *DoltDatabaseProvider) registerNewDatabase(ctx *sql.Context, name string, newEnv *env.DoltEnv) (err error) {
|
||||
// Creating normal database names with revision delimiters can create ambiguity in methods that do not have access
|
||||
// to some sort database table (e.g., client-side evaluations through server queries).
|
||||
if strings.ContainsAny(name, doltdb.DbRevisionDelimiter+doltdb.DbRevisionDelimiterAlias) {
|
||||
return sql.ErrWrongDBName.New(name)
|
||||
}
|
||||
|
||||
// This method MUST be called with the provider's mutex locked
|
||||
if err = lockutil.AssertRWMutexIsLocked(p.mu); err != nil {
|
||||
return fmt.Errorf("unable to register new database without database provider mutex being locked")
|
||||
@@ -1066,15 +1057,15 @@ func (p *DoltDatabaseProvider) invalidateDbStateInAllSessions(ctx *sql.Context,
|
||||
}
|
||||
|
||||
func (p *DoltDatabaseProvider) databaseForRevision(ctx *sql.Context, revisionQualifiedName string, requestedName string) (dsess.SqlDatabase, bool, error) {
|
||||
if !strings.Contains(revisionQualifiedName, doltdb.DbRevisionDelimiter) {
|
||||
if !strings.ContainsAny(revisionQualifiedName, doltdb.DbRevisionDelimiter+doltdb.DbRevisionDelimiterAlias) {
|
||||
return nil, false, nil
|
||||
}
|
||||
baseName, rev := doltdb.SplitRevisionDbName(revisionQualifiedName)
|
||||
|
||||
p.mu.RLock()
|
||||
srcDb, srcOk := p.databases[formatDbMapKeyName(baseName)]
|
||||
srcDb, ok := p.databases[formatDbMapKeyName(baseName)]
|
||||
p.mu.RUnlock()
|
||||
if !srcOk {
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
@@ -1449,70 +1440,52 @@ func (p *DoltDatabaseProvider) BaseDatabase(ctx *sql.Context, name string) (dses
|
||||
|
||||
// SessionDatabase implements dsess.SessionDatabaseProvider
|
||||
func (p *DoltDatabaseProvider) SessionDatabase(ctx *sql.Context, name string) (dsess.SqlDatabase, bool, error) {
|
||||
normalized, usesDelimiterAlias := doltdb.NormalizeRevisionDelimiter(name)
|
||||
baseName, revision := doltdb.SplitRevisionDbName(normalized)
|
||||
baseName, revision := doltdb.SplitRevisionDbName(strings.ToLower(name))
|
||||
revisionQualifiedName := formatDbMapKeyName(name)
|
||||
|
||||
var ok bool
|
||||
p.mu.RLock()
|
||||
db, ok := p.databases[strings.ToLower(baseName)]
|
||||
var rawDB dsess.SqlDatabase
|
||||
rawDBOk := false
|
||||
if usesDelimiterAlias {
|
||||
rawDB, rawDBOk = p.databases[strings.ToLower(name)]
|
||||
}
|
||||
db, ok := p.databases[baseName]
|
||||
standby := *p.isStandby
|
||||
p.mu.RUnlock()
|
||||
|
||||
var err error
|
||||
if usesDelimiterAlias && !rawDBOk {
|
||||
rawDB, err = p.databaseForClone(ctx, strings.ToLower(name))
|
||||
// Ignore error, revision needs to be evaluated first.
|
||||
rawDBOk = rawDB != nil && err == nil
|
||||
}
|
||||
|
||||
// If the database doesn't exist and this is a read replica, attempt to clone it from the remote
|
||||
if !ok {
|
||||
db, err = p.databaseForClone(ctx, strings.ToLower(baseName))
|
||||
if err != nil && !usesDelimiterAlias {
|
||||
db, err = p.databaseForClone(ctx, baseName)
|
||||
if err != nil || db == nil {
|
||||
return nil, false, err
|
||||
}
|
||||
ok = db != nil
|
||||
if !ok && !rawDBOk {
|
||||
return nil, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Some DB implementations don't support addressing by versioned names, so return directly if we have one of those
|
||||
if ok && !db.Versioned() {
|
||||
if !db.Versioned() {
|
||||
return wrapForStandby(db, standby), true, nil
|
||||
}
|
||||
|
||||
// Convert to a revision database before returning. If we got a non-qualified name, convert it to a qualified name
|
||||
// using the session's current head
|
||||
sess := dsess.DSessFromSess(ctx.Session)
|
||||
usingDefaultBranch := false
|
||||
var head string
|
||||
if ok && revision == "" {
|
||||
head, usingDefaultBranch, err = p.resolveCurrentOrDefaultHead(ctx, sess, db, baseName)
|
||||
head := ""
|
||||
if revision == "" {
|
||||
head, ok, err = dsess.DSessFromSess(ctx.Session).CurrentHead(ctx, baseName)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
normalized = baseName + doltdb.DbRevisionDelimiter + head
|
||||
}
|
||||
|
||||
db, ok, err = p.databaseForRevision(ctx, normalized, name)
|
||||
if (!ok || err != nil) && rawDBOk {
|
||||
if !rawDB.Versioned() {
|
||||
return wrapForStandby(rawDB, standby), true, nil
|
||||
}
|
||||
head, usingDefaultBranch, err = p.resolveCurrentOrDefaultHead(ctx, sess, rawDB, name)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
normalized = name + doltdb.DbRevisionDelimiter + head
|
||||
db, ok, err = p.databaseForRevision(ctx, normalized, name)
|
||||
|
||||
// A newly created session may not have any info on current head stored yet, in which case we get the default
|
||||
// branch for the db itself instead.
|
||||
if !ok {
|
||||
usingDefaultBranch = true
|
||||
head, err = dsess.DefaultHead(ctx, baseName, db)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
revisionQualifiedName = baseName + doltdb.DbRevisionDelimiter + head
|
||||
}
|
||||
|
||||
db, ok, err = p.databaseForRevision(ctx, revisionQualifiedName, name)
|
||||
if err != nil {
|
||||
if sql.ErrDatabaseNotFound.Is(err) && usingDefaultBranch {
|
||||
// We can return a better error message here in some cases
|
||||
@@ -1531,24 +1504,6 @@ func (p *DoltDatabaseProvider) SessionDatabase(ctx *sql.Context, name string) (d
|
||||
return wrapForStandby(db, standby), true, nil
|
||||
}
|
||||
|
||||
func (p *DoltDatabaseProvider) resolveCurrentOrDefaultHead(ctx *sql.Context, sess *dsess.DoltSession, db dsess.SqlDatabase, baseName string) (resolvedHead string, usedDefaultHead bool, err error) {
|
||||
resolvedHead, ok, err := sess.CurrentHead(ctx, baseName)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if ok {
|
||||
return resolvedHead, false, nil
|
||||
}
|
||||
|
||||
// A newly created session may not have any info on current head stored yet, in which case we get the default
|
||||
// branch for the db itself instead.
|
||||
resolvedHead, err = dsess.DefaultHead(ctx, baseName, db)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
return resolvedHead, true, nil
|
||||
}
|
||||
|
||||
// Function implements the FunctionProvider interface
|
||||
func (p *DoltDatabaseProvider) Function(_ *sql.Context, name string) (sql.Function, bool) {
|
||||
fn, ok := p.functions[strings.ToLower(name)]
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
// 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 dfunctions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/types"
|
||||
)
|
||||
|
||||
const ActiveRevisionFuncName = "active_revision"
|
||||
|
||||
type ActiveRevisionFunc struct{}
|
||||
|
||||
// NewActiveRevisionFunc creates a new ActiveRevisionFunc expression.
|
||||
func NewActiveRevisionFunc() sql.Expression {
|
||||
return &ActiveRevisionFunc{}
|
||||
}
|
||||
|
||||
// Eval implements the Expression interface.
|
||||
func (*ActiveRevisionFunc) Eval(ctx *sql.Context, _ sql.Row) (interface{}, error) {
|
||||
_, activeRevision, err := resolveSessionDatabaseIdentity(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if activeRevision == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return activeRevision, nil
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (*ActiveRevisionFunc) String() string {
|
||||
return "ACTIVE_REVISION()"
|
||||
}
|
||||
|
||||
// IsNullable implements the Expression interface.
|
||||
func (*ActiveRevisionFunc) IsNullable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Resolved implements the Expression interface.
|
||||
func (*ActiveRevisionFunc) Resolved() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Type implements the Expression interface.
|
||||
func (*ActiveRevisionFunc) Type() sql.Type {
|
||||
return types.Text
|
||||
}
|
||||
|
||||
// Children implements the Expression interface.
|
||||
func (*ActiveRevisionFunc) Children() []sql.Expression {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithChildren implements the Expression interface.
|
||||
func (f *ActiveRevisionFunc) WithChildren(children ...sql.Expression) (sql.Expression, error) {
|
||||
if len(children) != 0 {
|
||||
return nil, sql.ErrInvalidChildrenNumber.New(f, len(children), 0)
|
||||
}
|
||||
return NewActiveRevisionFunc(), nil
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
// 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 dfunctions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/types"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
|
||||
)
|
||||
|
||||
const BaseDatabaseFuncName = "base_database"
|
||||
|
||||
type BaseDatabaseFunc struct{}
|
||||
|
||||
// NewBaseDatabaseFunc creates a new BaseDatabaseFunc expression.
|
||||
func NewBaseDatabaseFunc() sql.Expression {
|
||||
return &BaseDatabaseFunc{}
|
||||
}
|
||||
|
||||
// Eval implements the Expression interface.
|
||||
func (*BaseDatabaseFunc) Eval(ctx *sql.Context, _ sql.Row) (interface{}, error) {
|
||||
baseDatabase, _, err := resolveSessionDatabaseIdentity(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if baseDatabase == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return baseDatabase, nil
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (*BaseDatabaseFunc) String() string {
|
||||
return "BASE_DATABASE()"
|
||||
}
|
||||
|
||||
// IsNullable implements the Expression interface.
|
||||
func (*BaseDatabaseFunc) IsNullable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Resolved implements the Expression interface.
|
||||
func (*BaseDatabaseFunc) Resolved() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Type implements the Expression interface.
|
||||
func (*BaseDatabaseFunc) Type() sql.Type {
|
||||
return types.Text
|
||||
}
|
||||
|
||||
// Children implements the Expression interface.
|
||||
func (*BaseDatabaseFunc) Children() []sql.Expression {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithChildren implements the Expression interface.
|
||||
func (f *BaseDatabaseFunc) WithChildren(children ...sql.Expression) (sql.Expression, error) {
|
||||
if len(children) != 0 {
|
||||
return nil, sql.ErrInvalidChildrenNumber.New(f, len(children), 0)
|
||||
}
|
||||
return NewBaseDatabaseFunc(), nil
|
||||
}
|
||||
|
||||
// resolveSessionDatabaseIdentity resolves the base database and active revision for the current session database.
|
||||
func resolveSessionDatabaseIdentity(ctx *sql.Context) (baseDatabase string, activeRevision string, err error) {
|
||||
dbName := ctx.GetCurrentDatabase()
|
||||
if dbName == "" {
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
dSess := dsess.DSessFromSess(ctx.Session)
|
||||
sessionDb, ok, err := dSess.Provider().SessionDatabase(ctx, dbName)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if !ok {
|
||||
// Non-Dolt databases can still be current.
|
||||
return dbName, "", nil
|
||||
}
|
||||
|
||||
baseDatabase = sessionDb.AliasedName()
|
||||
activeRevision = sessionDb.Revision()
|
||||
return baseDatabase, activeRevision, nil
|
||||
}
|
||||
@@ -22,8 +22,6 @@ var DoltFunctions = []sql.Function{
|
||||
sql.Function0{Name: VersionFuncName, Fn: NewVersion},
|
||||
sql.Function0{Name: StorageFormatFuncName, Fn: NewStorageFormat},
|
||||
sql.Function0{Name: ActiveBranchFuncName, Fn: NewActiveBranchFunc},
|
||||
sql.Function0{Name: BaseDatabaseFuncName, Fn: NewBaseDatabaseFunc},
|
||||
sql.Function0{Name: ActiveRevisionFuncName, Fn: NewActiveRevisionFunc},
|
||||
sql.Function2{Name: DoltMergeBaseFuncName, Fn: NewMergeBase},
|
||||
sql.Function2{Name: HasAncestorFuncName, Fn: NewHasAncestor},
|
||||
sql.Function1{Name: HashOfTableFuncName, Fn: NewHashOfTable},
|
||||
|
||||
@@ -175,19 +175,7 @@ func GetTableResolver(ctx *sql.Context, dbName string) (doltdb.TableResolver, er
|
||||
// lookupDbState is the private version of LookupDbState, returning a struct that has more information available than
|
||||
// the interface returned by the public method.
|
||||
func (d *DoltSession) lookupDbState(ctx *sql.Context, dbName string) (*branchState, bool, error) {
|
||||
dbName = strings.ToLower(dbName)
|
||||
baseName, rev := doltdb.SplitRevisionDbName(dbName)
|
||||
|
||||
// usesDelimiterAlias once normalized goes to false, so any `@` character will not be treated as a delimiter. Since
|
||||
// it has two meanings, either a revision or normal DB, we process twice.
|
||||
normalized, usesDelimiterAlias := doltdb.NormalizeRevisionDelimiter(dbName)
|
||||
if usesDelimiterAlias {
|
||||
state, ok, err := d.lookupDbState(ctx, normalized)
|
||||
if err == nil && ok {
|
||||
return state, ok, err
|
||||
}
|
||||
}
|
||||
|
||||
baseName, rev := doltdb.SplitRevisionDbName(strings.ToLower(dbName))
|
||||
d.mu.Lock()
|
||||
dbState, dbStateFound := d.dbStates[baseName]
|
||||
d.mu.Unlock()
|
||||
|
||||
@@ -1219,6 +1219,11 @@ func TestDoltScripts(t *testing.T) {
|
||||
RunDoltScriptsTest(t, harness)
|
||||
}
|
||||
|
||||
func TestDoltProcedureScripts(t *testing.T) {
|
||||
h := newDoltEnginetestHarness(t)
|
||||
RunDoltProcedureScriptsTest(t, h)
|
||||
}
|
||||
|
||||
func TestDoltTempTableScripts(t *testing.T) {
|
||||
harness := newDoltEnginetestHarness(t)
|
||||
RunDoltTempTableScripts(t, harness)
|
||||
|
||||
@@ -513,6 +513,8 @@ func RunStoredProceduresTest(t *testing.T, h DoltEnginetestHarness) {
|
||||
}
|
||||
|
||||
func RunDoltStoredProceduresTest(t *testing.T, h DoltEnginetestHarness) {
|
||||
DoltProcedureTests := append(DoltProcedureTests, DoltBackupProcedureScripts...)
|
||||
DoltProcedureTests = append(DoltProcedureTests, DoltStatusProcedureScripts...)
|
||||
for _, script := range DoltProcedureTests {
|
||||
func() {
|
||||
h := h.NewHarness(t)
|
||||
@@ -524,6 +526,8 @@ func RunDoltStoredProceduresTest(t *testing.T, h DoltEnginetestHarness) {
|
||||
}
|
||||
|
||||
func RunDoltStoredProceduresPreparedTest(t *testing.T, h DoltEnginetestHarness) {
|
||||
DoltProcedureTests := append(DoltProcedureTests, DoltBackupProcedureScripts...)
|
||||
DoltProcedureTests = append(DoltProcedureTests, DoltStatusProcedureScripts...)
|
||||
for _, script := range DoltProcedureTests {
|
||||
func() {
|
||||
h := h.NewHarness(t)
|
||||
|
||||
@@ -26,10 +26,6 @@ import (
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/env"
|
||||
)
|
||||
|
||||
func init() {
|
||||
DoltProcedureTests = append(DoltProcedureTests, DoltBackupProcedureScripts...)
|
||||
}
|
||||
|
||||
// fileUrl returns a file:// URL path.
|
||||
func fileUrl(path string) string {
|
||||
path = filepath.Join(os.TempDir(), path)
|
||||
@@ -957,3 +953,31 @@ END
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var DoltStatusProcedureScripts = []queries.ScriptTest{
|
||||
{
|
||||
Name: "dolt_status detached head is read-only clean",
|
||||
SetUpScript: []string{
|
||||
"call dolt_commit('--allow-empty', '-m', 'empty commit');",
|
||||
"call dolt_tag('tag1');",
|
||||
"set @head_hash = (select hashof('main') limit 1);",
|
||||
"set @status_by_hash = concat('select * from `mydb/', @head_hash, '`.dolt_status;');",
|
||||
"prepare status_by_hash from @status_by_hash;",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "select * from `mydb/tag1`.dolt_status;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "execute status_by_hash;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "select * from `information_schema`.dolt_status;",
|
||||
// Non-versioned database.
|
||||
ExpectedErr: sql.ErrTableNotFound,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -15,8 +15,11 @@
|
||||
package enginetest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/enginetest/queries"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/plan"
|
||||
"github.com/dolthub/go-mysql-server/sql/types"
|
||||
)
|
||||
|
||||
@@ -404,146 +407,77 @@ var DoltRevisionDbScripts = []queries.ScriptTest{
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "database revision specs: db revision delimiter alias '@' is ignored when no revision exists",
|
||||
SetUpScript: []string{
|
||||
"create database `mydb@branch1`;",
|
||||
"create table t1(t int);",
|
||||
"call dolt_commit('-Am', 'init t1');",
|
||||
"create database `test-10382`;",
|
||||
"use `test-10382`;",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "use `mydb@branch1`;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "drop database `test-10382`;",
|
||||
Expected: []sql.Row{{types.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "select database();",
|
||||
Expected: []sql.Row{{"mydb@branch1"}},
|
||||
},
|
||||
{
|
||||
Query: "show databases",
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb@branch1"}, {"mysql"}},
|
||||
},
|
||||
{
|
||||
Query: "set dolt_show_branch_databases = on;",
|
||||
Expected: []sql.Row{{types.NewOkResult(0)}},
|
||||
},
|
||||
{
|
||||
Query: "show databases",
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb/main"}, {"mydb@branch1"}, {"mydb@branch1/main"}, {"mysql"}},
|
||||
},
|
||||
{
|
||||
Query: "use `mydb@branch1`;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_branch('branch2');",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "use `mydb@branch1@branch2`;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "select database();",
|
||||
Expected: []sql.Row{{"mydb@branch1@branch2"}},
|
||||
},
|
||||
{
|
||||
Query: "use `mydb@branch1`;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "show databases",
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb/main"}, {"mydb@branch1"}, {"mydb@branch1/main"}, {"mydb@branch1/branch2"}, {"mysql"}},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_branch('branch@');",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "show databases",
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb/main"}, {"mydb@branch1"}, {"mydb@branch1/main"}, {"mydb@branch1/branch2"}, {"mydb@branch1/branch@"}, {"mysql"}},
|
||||
},
|
||||
{
|
||||
Query: "set dolt_show_branch_databases = off;",
|
||||
Expected: []sql.Row{{types.NewOkResult(0)}},
|
||||
},
|
||||
{
|
||||
Query: "show databases",
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb@branch1"}, {"mysql"}},
|
||||
},
|
||||
{
|
||||
Query: "select * from t1;",
|
||||
ExpectedErr: sql.ErrTableNotFound,
|
||||
},
|
||||
{
|
||||
Query: "use mydb;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "select * from t1;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "database revision specs: db revision delimiter alias '@'",
|
||||
SetUpScript: []string{
|
||||
"create table t01 (pk int primary key, c1 int);",
|
||||
"call dolt_add('.');",
|
||||
"call dolt_commit('-am', 'creating table t01 on main');",
|
||||
"insert into t01 values (1, 1), (2, 2);",
|
||||
"call dolt_commit('-am', 'adding rows to table t01 on main');",
|
||||
"call dolt_tag('tag1');",
|
||||
"create table t02 (pk int primary key, c1 int);",
|
||||
"call dolt_commit('-Am', 'creating table t02 on main');",
|
||||
"call dolt_tag('tag2');",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "create database `mydb@branch1`;",
|
||||
Expected: []sql.Row{{types.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "use mydb;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_branch('branch1');",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "insert into t01 values (3, 3);",
|
||||
Expected: []sql.Row{{types.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_commit('-am', 'adding rows to table t01');",
|
||||
SkipResultsCheck: true,
|
||||
Query: "create database `mydb@branch1`;",
|
||||
ExpectedErr: sql.ErrWrongDBName,
|
||||
ExpectedErrStr: "mydb@branch1",
|
||||
},
|
||||
{
|
||||
Query: "use `mydb@main`;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "show databases;",
|
||||
// The mydb@branch1 database is shown, not the revision `branch1` from `mydb` cause we're on `main`.
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb@branch1"}, {"mydb@main"}, {"mysql"}},
|
||||
// We want to show the revision database in-use as the original requested name.
|
||||
Query: "show databases;",
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb@main"}, {"mysql"}},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_checkout('-b', 'branch@');",
|
||||
Expected: []sql.Row{{0, "Switched to branch 'branch@'"}},
|
||||
},
|
||||
{
|
||||
Query: "use `mydb/branch@`",
|
||||
// The `/` delimiter takes precedence over the alias.
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "select database()",
|
||||
Expected: []sql.Row{{"mydb/branch@"}},
|
||||
},
|
||||
{
|
||||
Query: "drop database `mydb@branch@`",
|
||||
// The first index is processed first, allowing branch names with the @ character.
|
||||
ExpectedErrStr: fmt.Sprintf("unable to drop revision database: %s", "mydb@branch@"),
|
||||
},
|
||||
{
|
||||
Query: "use `mydb@branch@`;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_checkout('-b', 'branch1');",
|
||||
// dolt_checkout works from a revision database using the alias delimiter.
|
||||
Expected: []sql.Row{{0, "Switched to branch 'branch1'"}},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_branch('-D', 'branch@');",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "create table t01(pk int primary key, c1 int);",
|
||||
Expected: []sql.Row{{types.NewOkResult(0)}},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_commit('-Am', 'creating table t01 on branch1');",
|
||||
Expected: []sql.Row{{doltCommit}},
|
||||
},
|
||||
{
|
||||
Query: "use `mydb@branch1`;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "show databases;",
|
||||
// The revision branch1 is shown, not the `mydb@branch1` database.
|
||||
Query: "show databases;",
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb@branch1"}, {"mysql"}},
|
||||
},
|
||||
{
|
||||
Query: "select database();",
|
||||
// We want to see the revision shown in the format it was requested, this is not the literal db.
|
||||
Query: "select database();",
|
||||
Expected: []sql.Row{{"mydb@branch1"}},
|
||||
},
|
||||
{
|
||||
@@ -552,15 +486,54 @@ var DoltRevisionDbScripts = []queries.ScriptTest{
|
||||
},
|
||||
{
|
||||
Query: "show databases;",
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb/main"}, {"mydb@branch1"}, {"mysql"}},
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb@branch1"}, {"mydb/main"}, {"mysql"}},
|
||||
},
|
||||
{
|
||||
Query: "select active_branch();",
|
||||
Expected: []sql.Row{{"branch1"}},
|
||||
},
|
||||
{
|
||||
Query: "select * from `mydb@branch1`.t01;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "insert into `mydb@branch1`.t01 values (1, 10), (2, 20);",
|
||||
Expected: []sql.Row{{types.NewOkResult(2)}},
|
||||
},
|
||||
{
|
||||
Query: "select * from `mydb@branch1`.t01 order by pk;",
|
||||
Expected: []sql.Row{{1, 10}, {2, 20}},
|
||||
},
|
||||
{
|
||||
Query: "update `mydb@branch1`.t01 set c1 = 30 where pk = 2;",
|
||||
Expected: []sql.Row{{types.OkResult{
|
||||
Info: plan.UpdateInfo{Matched: 1, Updated: 1},
|
||||
RowsAffected: 1,
|
||||
}}},
|
||||
},
|
||||
{
|
||||
Query: "select * from `mydb@branch1`.t01 where pk = 2;",
|
||||
Expected: []sql.Row{{2, 30}},
|
||||
},
|
||||
{
|
||||
Query: "delete from `mydb@branch1`.t01 where pk = 1;",
|
||||
Expected: []sql.Row{{types.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "select * from `mydb@branch1`.t01 order by pk;",
|
||||
Expected: []sql.Row{{2, 30}},
|
||||
},
|
||||
{
|
||||
Query: "alter table `mydb@branch1`.t01 add index idx_t01_c1 (c1);",
|
||||
Expected: []sql.Row{{types.NewOkResult(0)}},
|
||||
},
|
||||
{
|
||||
Query: "select index_name, column_name from information_schema.statistics where table_schema = database() and table_name = 't01' and index_name = 'idx_t01_c1' order by seq_in_index;",
|
||||
Expected: []sql.Row{{"idx_t01_c1", "c1"}},
|
||||
},
|
||||
{
|
||||
Query: "select * from t01;",
|
||||
Expected: []sql.Row{{1, 1}, {2, 2}},
|
||||
Expected: []sql.Row{{2, 30}},
|
||||
},
|
||||
{
|
||||
Query: "select column_name from information_schema.columns where table_schema = database() and table_name = 't01' order by ordinal_position;",
|
||||
@@ -570,26 +543,6 @@ var DoltRevisionDbScripts = []queries.ScriptTest{
|
||||
Query: "select table_name from information_schema.tables where table_schema = database() and table_name = 't01';",
|
||||
Expected: []sql.Row{{"t01"}},
|
||||
},
|
||||
{
|
||||
Query: "use mydb;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "show databases;",
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb/main"}, {"mydb/branch1"}, {"mydb@branch1"}, {"mydb@branch1/main"}, {"mysql"}},
|
||||
},
|
||||
{
|
||||
Query: "select * from `mydb@branch1`.t01;",
|
||||
Expected: []sql.Row{{1, 1}, {2, 2}},
|
||||
},
|
||||
{
|
||||
Query: "select * from `mydb@tag1`.t01;",
|
||||
Expected: []sql.Row{{1, 1}, {2, 2}},
|
||||
},
|
||||
{
|
||||
Query: "use `mydb@branch1`;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "create table parent(id int primary key);",
|
||||
Expected: []sql.Row{{types.NewOkResult(0)}},
|
||||
@@ -612,7 +565,7 @@ var DoltRevisionDbScripts = []queries.ScriptTest{
|
||||
},
|
||||
{
|
||||
Query: "show databases;",
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb/main"}, {"mydb/branch1"}, {"mydb@branch1"}, {"mydb@branch1/main"}, {"mysql"}},
|
||||
Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mydb/branch1"}, {"mydb/main"}, {"mysql"}},
|
||||
},
|
||||
{
|
||||
Query: "select database();",
|
||||
@@ -620,17 +573,12 @@ var DoltRevisionDbScripts = []queries.ScriptTest{
|
||||
},
|
||||
{
|
||||
Query: "select * from t01;",
|
||||
Expected: []sql.Row{{1, 1}, {2, 2}},
|
||||
Expected: []sql.Row{{2, 30}},
|
||||
},
|
||||
{
|
||||
Query: "select column_name from information_schema.columns where table_schema = database() and table_name = 't01' order by ordinal_position;",
|
||||
Expected: []sql.Row{{"pk"}, {"c1"}},
|
||||
},
|
||||
{
|
||||
Query: "drop database `mydb@branch1`;",
|
||||
// The name above can be resolved to a real revision so we error out, keeping parity with CREATE below.
|
||||
ExpectedErrStr: "unable to drop revision database: mydb@branch1",
|
||||
},
|
||||
{
|
||||
Query: "select table_name from information_schema.tables where table_schema = database() and table_name = 't01';",
|
||||
Expected: []sql.Row{{"t01"}},
|
||||
@@ -644,65 +592,30 @@ var DoltRevisionDbScripts = []queries.ScriptTest{
|
||||
Expected: []sql.Row{{1, 1}},
|
||||
},
|
||||
{
|
||||
Query: "use mydb;",
|
||||
Query: "drop table `mydb@branch1`.child;",
|
||||
Expected: []sql.Row{{types.NewOkResult(0)}},
|
||||
},
|
||||
{
|
||||
Query: "select table_name from information_schema.tables where table_schema = database() and table_name = 'child';",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_branch('-D', 'branch1');",
|
||||
Expected: []sql.Row{{0}},
|
||||
Query: "select * from `mydb@tag2`.t02;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "drop database `mydb@branch1`;",
|
||||
Expected: []sql.Row{{types.NewOkResult(1)}},
|
||||
Query: "use `mydb/branch1`;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_branch('branch1');",
|
||||
Expected: []sql.Row{{0}},
|
||||
Query: "select * from t01;",
|
||||
Expected: []sql.Row{{2, 30}},
|
||||
},
|
||||
{
|
||||
Query: "create database `mydb@branch1`;",
|
||||
// This is a result of GMS' internal call to the providers' HasDatabase
|
||||
ExpectedErrStr: "can't create database mydb@branch1; database exists",
|
||||
},
|
||||
{
|
||||
Query: "call dolt_branch('-D', 'branch1');",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "create database `mydb@branch1`;",
|
||||
Expected: []sql.Row{{types.NewOkResult(1)}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "database revision specs: base_database and active_revision",
|
||||
SetUpScript: []string{
|
||||
"create table t1(pk int primary key);",
|
||||
"call dolt_add('.');",
|
||||
"call dolt_commit('-am', 'init');",
|
||||
"call dolt_branch('branch1');",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "select base_database(), active_revision();",
|
||||
Expected: []sql.Row{{"mydb", "main"}},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_checkout('branch1');",
|
||||
Expected: []sql.Row{{0, "Switched to branch 'branch1'"}},
|
||||
},
|
||||
{
|
||||
Query: "select base_database(), active_revision();",
|
||||
Expected: []sql.Row{{"mydb", "branch1"}},
|
||||
},
|
||||
{
|
||||
Query: "use `mydb@branch1`;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "select base_database(), active_revision();",
|
||||
Expected: []sql.Row{{"mydb", "branch1"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -711,13 +624,13 @@ var DoltRevisionDbScripts = []queries.ScriptTest{
|
||||
"create table t01 (pk int primary key, c1 int);",
|
||||
"call dolt_add('.');",
|
||||
"call dolt_commit('-am', 'creating table t01 on main');",
|
||||
"set @h = (select hashof('main') limit 1);",
|
||||
"set @use_sql = concat('use `mydb@', @h, '`');",
|
||||
"set @h1 = (select hashof('main') limit 1);",
|
||||
"set @use_sql = concat('use `mydb@', @h1, '`');",
|
||||
"prepare use_stmt from @use_sql;",
|
||||
"insert into t01 values (1, 1), (2, 2);",
|
||||
"call dolt_commit('-am', 'adding rows to table t01 on main');",
|
||||
"set @h = (select hashof('main') limit 1);",
|
||||
"set @select_sql = concat('select * from `mydb@', @h, '`.t01');",
|
||||
"set @h2 = (select hashof('main') limit 1);",
|
||||
"set @select_sql = concat('select * from `mydb@', @h2, '`.t01');",
|
||||
"prepare select_stmt from @select_sql;",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
@@ -729,6 +642,10 @@ var DoltRevisionDbScripts = []queries.ScriptTest{
|
||||
Query: "select length(database());",
|
||||
Expected: []sql.Row{{37}},
|
||||
},
|
||||
{
|
||||
Query: "select @h1;",
|
||||
Expected: []sql.Row{{doltCommit}},
|
||||
},
|
||||
{
|
||||
Query: "select * from t01;",
|
||||
Expected: []sql.Row{},
|
||||
|
||||
@@ -6,47 +6,56 @@ set env(NO_COLOR) 1
|
||||
|
||||
spawn dolt sql
|
||||
|
||||
expect_with_defaults {>} { send "create database `mydb@branch1`;\r" }
|
||||
expect_with_defaults {>} { send "create schema `mydb@branch1`;\r" }
|
||||
|
||||
expect_with_defaults {>} { send "use `mydb@branch1`;\r" }
|
||||
# Test twice to make sure no directory is leftover.
|
||||
expect_with_defaults_after {Incorrect database name 'mydb@branch1'} {>} { send "create schema `mydb@branch1`;\r" }
|
||||
|
||||
expect_with_defaults_after {Database Changed} {mydb@branch1/main\*?>} { send "create schema `mydb`;\r" }
|
||||
expect_with_defaults_after {Incorrect database name 'mydb@branch1'} {>} { send "create schema mydb;\r" }
|
||||
|
||||
expect_with_defaults {>} { send "use mydb;\r" }
|
||||
|
||||
expect_with_defaults {>} { send "call dolt_branch('branch1');\r" }
|
||||
expect_with_defaults_after {Database Changed} {mydb/main>} { send "call dolt_checkout('-b', 'branch1');\r" }
|
||||
|
||||
expect_with_defaults {>} { send "use \`mydb/branch1\`;\r" }
|
||||
expect_with_defaults {mydb/main>} { send "use `mydb@branch1`;\r" }
|
||||
|
||||
expect_with_defaults {>} { send "show databases;\r" }
|
||||
expect_with_defaults_after {Database Changed} {mydb/branch1>} { send "use \`mydb/branch1\`;\r" }
|
||||
|
||||
expect_with_defaults_after {rows in set} {mydb/branch1\*?>} { send "use mydb;\r" }
|
||||
expect_with_defaults {mydb/branch1>} { send "show databases;\r" }
|
||||
|
||||
expect_with_defaults {>} { send "use \`mydb@branch1\`;\r" }
|
||||
expect_with_defaults_after {rows in set} {mydb/branch1>} { send "use `mydb@branch1`;\r" }
|
||||
|
||||
expect_with_defaults_after {Database Changed} {mydb/branch1\*?>} { send "set @h = (select hashof('branch1') limit 1);\r" }
|
||||
expect_with_defaults_after {Database Changed} {mydb/branch1>} { send "select database();\r" }
|
||||
|
||||
expect_with_defaults {>} { send "set @use_sql = concat('use `mydb@', @h, '`');\r" }
|
||||
expect_with_defaults_after {mydb@branch1} {mydb/branch1>} { send "set @h = (select hashof('branch1') limit 1);\r" }
|
||||
|
||||
expect_with_defaults {>} { send "prepare use_stmt from @use_sql;\r" }
|
||||
expect_with_defaults {mydb/branch1>} { send "call dolt_tag('tag1');\r" }
|
||||
|
||||
expect_with_defaults {>} { send "create table t1(i int);\r" }
|
||||
expect_with_defaults {mydb/branch1>} { send "set @use_sql = concat('use `mydb@', @h, '`');\r" }
|
||||
|
||||
expect_with_defaults {>} { send "call dolt_commit('-Am', 'create table t1');\r" }
|
||||
expect_with_defaults {mydb/branch1>} { send "prepare use_stmt from @use_sql;\r" }
|
||||
|
||||
expect_with_defaults {>} { send "show tables;\r" }
|
||||
expect_with_defaults {mydb/branch1>} { send "create table t1(i int);\r" }
|
||||
|
||||
expect_with_defaults_after {t1} {>} { send "execute use_stmt;\r" }
|
||||
expect_with_defaults {mydb/branch1*>} { send "call dolt_commit('-Am', 'create table t1');\r" }
|
||||
|
||||
expect_with_defaults_after {Empty set} {mydb/[0-9a-v]{32}\*?>} { send "show tables;\r" }
|
||||
expect_with_defaults {mydb/branch1>} { send "show tables;\r" }
|
||||
|
||||
expect_with_defaults_after {Empty set} {mydb/[0-9a-v]{32}\*?>} { send "create database `mydb@branch1`;\r" }
|
||||
expect_with_defaults_after {t1} {mydb/branch1>} { send "execute use_stmt;\r" }
|
||||
|
||||
expect_with_defaults_after {database exists} {>} { send "use `mydb`;\r" }
|
||||
expect_with_defaults_after {Empty set} {mydb/[0-9a-v]{32}>} { send "show tables;\r" }
|
||||
|
||||
expect_with_defaults {>} { send "call dolt_checkout('branch1');\r" }
|
||||
expect_with_defaults_after {Empty set} {mydb/[0-9a-v]{32}>} { send "select database();\r" }
|
||||
|
||||
expect_with_defaults_after {Switched to branch 'branch1'} {>} { send "exit;\r" }
|
||||
expect_with_defaults_after {mydb@[0-9a-v]{32}} {mydb/[0-9a-v]{32}>} {send "create database `mydb@branch1`;\r" }
|
||||
|
||||
expect_with_defaults_after {database exists} {mydb/[0-9a-v]{32}>} { send "use `mydb@tag1`;\r" }
|
||||
|
||||
expect_with_defaults_after {Database Changed} {mydb/tag1>} { send "show tables;\r" }
|
||||
|
||||
expect_with_defaults_after {Empty set} {mydb/tag1>} { send "select database();\r" }
|
||||
|
||||
expect_with_defaults_after {mydb@tag1} {mydb/tag1>} { send "exit;\r" }
|
||||
|
||||
expect eof
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user