项目文件夹

文件
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

270 行
8.8 KiB
Go

// Copyright 2019 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 commands
import (
"context"
"path"
"sync"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/env/actions"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
"github.com/dolthub/dolt/go/libraries/utils/earl"
"github.com/dolthub/dolt/go/store/datas/pull"
"github.com/dolthub/dolt/go/store/hash"
"github.com/dolthub/dolt/go/store/types"
)
const (
dirParamName = "dir"
)
var readTablesDocs = cli.CommandDocumentationContent{
ShortDesc: "Fetch table(s) at a specific commit into a new dolt repo",
LongDesc: "A shallow clone operation will retrieve the state of table(s) from a remote repository at a given commit. " +
"Retrieved data is placed into the working state of a newly created local Dolt repository. Changes to the data cannot " +
"be submitted back to the remote repository, and the shallow clone cannot be converted into a regular clone of a " +
"repository.",
Synopsis: []string{
"[--dir <directory>] {{.LessThan}}remote-url{{.GreaterThan}} {{.LessThan}}commit{{.GreaterThan}} [{{.LessThan}}table{{.GreaterThan}}...]",
},
}
// ReadTablesCmd is the implementation of the shallow-clone command
type ReadTablesCmd struct{}
// Name is returns the name of the Dolt cli command. This is what is used on the command line to invoke the command
func (cmd ReadTablesCmd) Name() string {
return "read-tables"
}
// Description returns a description of the command
func (cmd ReadTablesCmd) Description() string {
return readTablesDocs.ShortDesc
}
func (cmd ReadTablesCmd) Docs() *cli.CommandDocumentation {
ap := cmd.ArgParser()
return cli.NewCommandDocumentation(readTablesDocs, ap)
}
func (cmd ReadTablesCmd) RequiresRepo() bool {
return false
}
func (cmd ReadTablesCmd) ArgParser() *argparser.ArgParser {
ap := argparser.NewArgParserWithVariableArgs(cmd.Name())
ap.ArgListHelp = [][2]string{
{"remote-repo", "Remote repository to retrieve data from"},
{"commit", "Branch or commit hash representing a point in time to retrieve tables from"},
{"table", " Optional tables to retrieve. If omitted, all tables are retrieved."},
}
ap.SupportsString(dirParamName, "d", "directory", "directory to create and put retrieved table data.")
ap.SupportsString(gitRefFlag, "", "ref", "Git ref to use as the Dolt data ref for git remotes (default: refs/dolt/data).")
return ap
}
// Exec executes the command
func (cmd ReadTablesCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int {
ap := cmd.ArgParser()
help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, readTablesDocs, ap))
apr := cli.ParseArgsOrDie(ap, args, help)
if apr.NArg() < 2 {
return HandleVErrAndExitCode(errhand.BuildDError("Missing required arguments").SetPrintUsage().Build(), usage)
}
urlStr := apr.Arg(0)
commitStr := apr.Arg(1)
tblNames := apr.Args[2:]
_, err := earl.Parse(urlStr)
if err != nil {
if normalized, ok, nerr := env.NormalizeGitRemoteUrl(urlStr); nerr == nil && ok {
urlStr = normalized
} else {
return HandleVErrAndExitCode(errhand.BuildDError("Invalid remote url").AddCause(err).Build(), usage)
}
}
dir := apr.GetValueOrDefault(dirParamName, path.Base(urlStr))
if dir == "" {
return HandleVErrAndExitCode(errhand.BuildDError(`parameter %s has an invalid value of ""`, dirParamName).Build(), usage)
}
scheme, remoteUrl, err := env.GetAbsRemoteUrl(dEnv.FS, dEnv.Config, urlStr)
if err != nil {
return HandleVErrAndExitCode(errhand.BuildDError("Invalid remote url").AddCause(err).Build(), usage)
}
remoteUrlParams, verr := parseRemoteArgs(apr, scheme, remoteUrl)
if verr != nil {
return HandleVErrAndExitCode(verr, usage)
}
srcDB, srcRoot, verr := getRemoteDBAtCommit(ctx, remoteUrl, remoteUrlParams, commitStr, dEnv)
if verr != nil {
return HandleVErrAndExitCode(verr, usage)
}
branches, err := srcDB.GetBranches(ctx)
if verr != nil {
BuildVerrAndExit("Failed to get remote branches", err)
}
dEnv, verr = initializeShallowCloneRepo(ctx, dEnv, srcDB.Format(), dir, env.GetDefaultBranch(dEnv, branches))
if verr != nil {
return HandleVErrAndExitCode(verr, usage)
}
destRoot, err := dEnv.WorkingRoot(ctx)
if err != nil {
return BuildVerrAndExit("Failed to read working root", err)
}
if len(tblNames) == 0 {
tblNames, err = srcRoot.GetTableNames(ctx, doltdb.DefaultSchemaName, true)
if err != nil {
return BuildVerrAndExit("Unable to read tables.", err)
}
}
for _, tblName := range tblNames {
destRoot, verr = pullTableValue(ctx, dEnv, srcDB, srcRoot, destRoot, downloadLanguage, doltdb.TableName{Name: tblName}, commitStr)
if verr != nil {
return HandleVErrAndExitCode(verr, usage)
}
}
err = dEnv.UpdateWorkingRoot(ctx, destRoot)
if err != nil {
return BuildVerrAndExit("Unable to update the working root for local database.", err)
}
err = dbfactory.ClearDatabaseInProgress(dEnv.FS)
if err != nil {
return BuildVerrAndExit("Unable to finish creating the local database.", err)
}
return 0
}
func pullTableValue(ctx context.Context, dEnv *env.DoltEnv, srcDB *doltdb.DoltDB, srcRoot, destRoot doltdb.RootValue, language progLanguage, tblName doltdb.TableName, commitStr string) (doltdb.RootValue, errhand.VerboseError) {
tbl, ok, err := srcRoot.GetTable(ctx, tblName)
if !ok {
return nil, errhand.BuildDError("No table named '%s' at '%s'", tblName, commitStr).Build()
} else if err != nil {
return nil, errhand.BuildDError("Failed reading table '%s' from remote database.", tblName).AddCause(err).Build()
}
tblHash, err := tbl.HashOf()
if err != nil {
return nil, errhand.BuildDError("Unable to read from remote database.").AddCause(err).Build()
}
tmpDir, err := dEnv.TempTableFilesDir()
if err != nil {
return nil, errhand.BuildDError("error: ").AddCause(err).Build()
}
var wg sync.WaitGroup
statsCh := make(chan pull.Stats, 128)
wg.Go(func() {
processStats(language, statsCh)
})
wg.Go(func() {
defer close(statsCh)
cli.Println("Retrieving", tblName)
err = dEnv.DoltDB(ctx).PullChunks(ctx, tmpDir, srcDB, []hash.Hash{tblHash}, statsCh, nil)
})
wg.Wait()
if err != nil {
return nil, errhand.BuildDError("Failed reading chunks for remote table '%s' at '%s'", tblName, commitStr).AddCause(err).Build()
}
destRoot, err = destRoot.SetTableHash(ctx, tblName, tblHash)
if err != nil {
return nil, errhand.BuildDError("Unable to write to local database.").AddCause(err).Build()
}
return destRoot, nil
}
func getRemoteDBAtCommit(ctx context.Context, remoteUrl string, remoteUrlParams map[string]string, commitStr string, dEnv *env.DoltEnv) (*doltdb.DoltDB, doltdb.RootValue, errhand.VerboseError) {
cacheRoot, _ := dEnv.GitCacheRoot()
_, srcDB, verr := createRemote(ctx, "temp", remoteUrl, remoteUrlParams, dEnv, cacheRoot)
if verr != nil {
return nil, nil, verr
}
cs, err := doltdb.NewCommitSpec(commitStr)
if err != nil {
return nil, nil, errhand.BuildDError("Invalid Commit '%s'", commitStr).Build()
}
optCmt, err := srcDB.Resolve(ctx, cs, nil)
if err != nil {
return nil, nil, errhand.BuildDError("Failed to find commit '%s'", commitStr).Build()
}
cm, ok := optCmt.ToCommit()
if !ok {
return nil, nil, errhand.BuildDError("%s", doltdb.ErrGhostCommitEncountered.Error()).Build()
}
srcRoot, err := cm.GetRootValue(ctx)
if err != nil {
return nil, nil, errhand.BuildDError("Failed to read from database").AddCause(err).Build()
}
return srcDB, srcRoot, nil
}
func initializeShallowCloneRepo(ctx context.Context, dEnv *env.DoltEnv, nbf *types.NomsBinFormat, dir, branchName string) (*env.DoltEnv, errhand.VerboseError) {
var err error
dEnv, err = actions.EnvForClone(ctx, nbf, env.NoRemote, dir, dEnv.FS, dEnv.Version, env.GetCurrentUserHomeDir)
if err != nil {
return nil, errhand.VerboseErrorFromError(err)
}
err = actions.InitEmptyClonedRepo(ctx, dEnv)
if err != nil {
return nil, errhand.BuildDError("Unable to initialize repo.").AddCause(err).Build()
}
err = dEnv.InitializeRepoState(ctx, branchName)
if err != nil {
return nil, errhand.BuildDError("Unable to initialize repo.").AddCause(err).Build()
}
return dEnv, nil
}