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

222 lines
7.0 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"
"fmt"
"github.com/dolthub/go-mysql-server/sql"
"github.com/gocraft/dbr/v2"
"github.com/gocraft/dbr/v2/dialect"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
eventsapi "github.com/dolthub/eventsapi_schema/dolt/services/eventsapi/v1alpha1"
)
var lsDocs = cli.CommandDocumentationContent{
ShortDesc: "List tables",
LongDesc: `With no arguments lists the tables in the current working set but if a commit is specified it will list the tables in that commit. If the {{.EmphasisLeft}}--verbose{{.EmphasisRight}} flag is provided a row count of the table will also be displayed.
If the {{.EmphasisLeft}}--system{{.EmphasisRight}} flag is supplied this will show the dolt system tables which are queryable with SQL.
If the {{.EmphasisLeft}}--all{{.EmphasisRight}} flag is supplied both user and system tables will be printed.
`,
Synopsis: []string{
"[--options] [{{.LessThan}}commit{{.GreaterThan}}]",
},
}
type LsCmd 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 LsCmd) Name() string {
return "ls"
}
// Description returns a description of the command
func (cmd LsCmd) Description() string {
return "List tables in the working set."
}
func (cmd LsCmd) Docs() *cli.CommandDocumentation {
ap := cmd.ArgParser()
return cli.NewCommandDocumentation(lsDocs, ap)
}
func (cmd LsCmd) ArgParser() *argparser.ArgParser {
ap := argparser.NewArgParserWithMaxArgs(cmd.Name(), 1)
ap.SupportsFlag(cli.VerboseFlag, "v", "show the hash of the table and row count")
ap.SupportsFlag(cli.SystemFlag, "s", "show system tables")
ap.SupportsFlag(cli.AllFlag, "a", "show user and system tables")
return ap
}
// EventType returns the type of the event to log
func (cmd LsCmd) EventType() eventsapi.ClientEventType {
return eventsapi.ClientEventType_LS
}
// Exec executes the command
func (cmd LsCmd) 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, lsDocs, ap))
apr := cli.ParseArgsOrDie(ap, args, help)
if apr.Contains(cli.SystemFlag) && apr.Contains(cli.AllFlag) {
verr := errhand.BuildDError("--%s and --%s are mutually exclusive", cli.SystemFlag, cli.AllFlag).SetPrintUsage().Build()
return HandleVErrAndExitCode(verr, usage)
}
queryist, err := cliCtx.QueryEngine(ctx)
if err != nil {
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
query, err := constructInterpolatedDoltLsQuery(apr)
if err != nil {
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
_, _, _, err = queryist.Queryist.Query(queryist.Context, "set @@dolt_show_system_tables = 1")
if err != nil {
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
rows, err := cli.GetRowsForSql(queryist.Queryist, queryist.Context, query)
if err != nil {
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
_, _, _, err = queryist.Queryist.Query(queryist.Context, "set @@dolt_show_system_tables = 0")
if err != nil {
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
var userTables []string
var systemTables []string
for _, row := range rows {
tableName := row[0].(string)
if doltdb.HasDoltPrefix(tableName) || doltdb.HasDoltCIPrefix(tableName) {
systemTables = append(systemTables, tableName)
} else {
userTables = append(userTables, tableName)
}
}
if apr.Contains(cli.AllFlag) {
err = printUserTables(userTables, apr, queryist.Queryist, queryist.Context)
if err != nil {
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
err = printSystemTables(systemTables)
if err != nil {
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
} else if apr.Contains(cli.SystemFlag) {
err = printSystemTables(systemTables)
if err != nil {
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
} else {
err = printUserTables(userTables, apr, queryist.Queryist, queryist.Context)
if err != nil {
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
}
return HandleVErrAndExitCode(nil, usage)
}
// constructInterpolatedDoltLsQuery generates the sql query necessary to list all tables and interpolates this query
// to prevent sql injection
func constructInterpolatedDoltLsQuery(apr *argparser.ArgParseResults) (string, error) {
query := "show tables"
if apr.NArg() == 1 {
query += " as of ?"
interpolatedQuery, err := dbr.InterpolateForDialect(query, []interface{}{apr.Arg(0)}, dialect.MySQL)
if err != nil {
return "", err
}
return interpolatedQuery, nil
} else {
return query, nil
}
}
func printUserTables(tableNames []string, apr *argparser.ArgParseResults, queryist cli.Queryist, sqlCtx *sql.Context) error {
var label string
if apr.NArg() == 0 {
label = "working set"
} else {
query := fmt.Sprintf("select hashof('%s')", apr.Arg(0))
row, err := cli.GetRowsForSql(queryist, sqlCtx, query)
if err != nil {
return err
}
label = row[0][0].(string)
}
if len(tableNames) == 0 {
cli.Printf("No tables in %s\n", label)
return nil
}
cli.Printf("Tables in %s:\n", label)
for _, tbl := range tableNames {
if apr.Contains(cli.VerboseFlag) {
err := printTableVerbose(tbl, queryist, sqlCtx)
if err != nil {
return err
}
} else {
cli.Println("\t", tbl)
}
}
return nil
}
func printTableVerbose(table string, queryist cli.Queryist, sqlCtx *sql.Context) error {
query := fmt.Sprintf("select count(*) from `%s`", table)
row, err := cli.GetRowsForSql(queryist, sqlCtx, query)
if err != nil {
return err
}
if cnt, ok := row[0][0].(int64); ok {
cli.Println(fmt.Sprintf("\t%-20s %d rows", table, cnt))
} else if cnt, ok := row[0][0].(string); ok {
// remote execution returns result as a string
cli.Println(fmt.Sprintf("\t%-20s %s rows", table, cnt))
} else {
return fmt.Errorf("unexpected type for count: %T", row[0][0])
}
return nil
}
func printSystemTables(tableNames []string) error {
cli.Println("System tables:")
for _, tbl := range tableNames {
cli.Println("\t", tbl)
}
return nil
}