Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ golang.org/x/sys - https://github.com/golang/sys
Copyright 2009 The Go Authors.
License - https://github.com/golang/sys/blob/master/LICENSE

golang.org/x/term - https://github.com/golang/term
Copyright 2009 The Go Authors.
License - https://github.com/golang/term/blob/master/LICENSE

golang.org/x/text - https://github.com/golang/text
Copyright 2009 The Go Authors.
License - https://github.com/golang/text/blob/master/LICENSE
Expand Down
1 change: 1 addition & 0 deletions acceptance/pipelines/databricks-cli-help/output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Main Commands
logs Retrieve events for a pipeline
open Open a pipeline in the browser
run Run a pipeline
show Preview a table's columns and sample rows
stop Stop a pipeline

Management Commands
Expand Down
3 changes: 3 additions & 0 deletions acceptance/pipelines/show/basic/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions acceptance/pipelines/show/basic/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

=== table output

>>> [CLI] pipelines show main.default.people
id name
-- -----
1 alice
2 bob

2 rows

=== json output

>>> [CLI] pipelines show main.default.people --output json
[
{
"id": "1",
"name": "alice"
},
{
"id": "2",
"name": "bob"
}
]

=== invalid table name

>>> [CLI] pipelines show people
Error: invalid table name "people": expected catalog.schema.table (Unity Catalog) or schema.table (legacy Hive metastore)

=== wire SQL pins quoting and default limit

>>> print_requests.py //api/2.0/sql/statements
{
"method": "POST",
"path": "/api/2.0/sql/statements",
"body": {
"disposition": "INLINE",
"format": "JSON_ARRAY",
"on_wait_timeout": "CONTINUE",
"statement": "SELECT * FROM `main`.`default`.`people` LIMIT 10",
"wait_timeout": "10s",
"warehouse_id": "wh-test"
}
}
{
"method": "POST",
"path": "/api/2.0/sql/statements",
"body": {
"disposition": "INLINE",
"format": "JSON_ARRAY",
"on_wait_timeout": "CONTINUE",
"statement": "SELECT * FROM `main`.`default`.`people` LIMIT 10",
"wait_timeout": "10s",
"warehouse_id": "wh-test"
}
}
11 changes: 11 additions & 0 deletions acceptance/pipelines/show/basic/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
title "table output\n"
trace $CLI pipelines show main.default.people

title "json output\n"
trace $CLI pipelines show main.default.people --output json

title "invalid table name\n"
musterr trace $CLI pipelines show people

title "wire SQL pins quoting and default limit\n"
trace print_requests.py //api/2.0/sql/statements
21 changes: 21 additions & 0 deletions acceptance/pipelines/show/basic/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
RecordRequests = true

[EnvMatrix]
DATABRICKS_BUNDLE_ENGINE = ["direct"]

[Env]
DATABRICKS_WAREHOUSE_ID = "wh-test"

# The testserver's SQL Statement Execution API is matcher-driven and fails
# unmatched statements by default; stub a canned result so the golden shows real
# columns and rows. The SDK POSTs to the path without a trailing slash.
[[Server]]
Pattern = "POST /api/2.0/sql/statements"
Response.Body = '''
{
"statement_id": "s1",
"status": {"state": "SUCCEEDED"},
"manifest": {"schema": {"columns": [{"name": "id"}, {"name": "name"}]}, "total_chunk_count": 1},
"result": {"data_array": [["1", "alice"], ["2", "bob"]]}
}
'''
3 changes: 3 additions & 0 deletions acceptance/pipelines/show/help/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions acceptance/pipelines/show/help/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

=== help
>>> [CLI] pipelines show --help
Preview a table's columns and sample rows on a SQL warehouse.

TABLE is a fully-qualified name: catalog.schema.table (Unity Catalog) or
schema.table (legacy Hive metastore).

Usage:
databricks pipelines show TABLE [flags]

Flags:
-h, --help help for show
-n, --limit int Maximum number of rows to fetch. (default 10)
--warehouse-id string SQL warehouse to run the query on. Defaults to DATABRICKS_WAREHOUSE_ID or a workspace default.

Global Flags:
--debug enable debug logging
-o, --output type output type: text or json (default text)
-p, --profile string ~/.databrickscfg profile
-t, --target string bundle target to use (if applicable)
--var strings set values for variables defined in project config. Example: --var="foo=bar"
4 changes: 4 additions & 0 deletions acceptance/pipelines/show/help/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Pin the command tree so any change to the command or its help shows up as a diff here.

title "help"
trace $CLI pipelines show --help
3 changes: 3 additions & 0 deletions acceptance/pipelines/show/help/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# --help prints without authenticating, so no server stubs are needed.
[EnvMatrix]
DATABRICKS_BUNDLE_ENGINE = ["direct"]
3 changes: 3 additions & 0 deletions acceptance/pipelines/show/rendering/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions acceptance/pipelines/show/rendering/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

=== table output: long note truncated, ragged row blank cell

>>> [CLI] pipelines show main.default.people
name id note
----- -- ----------------------------------------
alice 1 this is a very long note value that e...
bob 2

2 rows

=== json output: column order preserved, ragged row padded with null

>>> [CLI] pipelines show main.default.people --output json
[
{
"name": "alice",
"id": "1",
"note": "this is a very long note value that exceeds forty display cells"
},
{
"name": "bob",
"id": "2",
"note": null
}
]
5 changes: 5 additions & 0 deletions acceptance/pipelines/show/rendering/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
title "table output: long note truncated, ragged row blank cell\n"
trace $CLI pipelines show main.default.people

title "json output: column order preserved, ragged row padded with null\n"
trace $CLI pipelines show main.default.people --output json
20 changes: 20 additions & 0 deletions acceptance/pipelines/show/rendering/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
RecordRequests = false

[EnvMatrix]
DATABRICKS_BUNDLE_ENGINE = ["direct"]

[Env]
DATABRICKS_WAREHOUSE_ID = "wh-test"

# Non-alphabetical columns test JSON order preservation; the ragged second row tests
# null padding, and the long note tests cell truncation.
[[Server]]
Pattern = "POST /api/2.0/sql/statements"
Response.Body = '''
{
"statement_id": "s1",
"status": {"state": "SUCCEEDED"},
"manifest": {"schema": {"columns": [{"name": "name"}, {"name": "id"}, {"name": "note"}]}, "total_chunk_count": 1},
"result": {"data_array": [["alice", "1", "this is a very long note value that exceeds forty display cells"], ["bob", "2"]]}
}
'''
1 change: 1 addition & 0 deletions cmd/pipelines/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ func Commands() []*cobra.Command {
historyCommand(),
logsCommand(),
openCommand(),
showCommand(),
}
}
110 changes: 110 additions & 0 deletions cmd/pipelines/show.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package pipelines

import (
"context"
"errors"
"fmt"
"os"
"os/signal"
"syscall"

"github.com/databricks/cli/cmd/root"
"github.com/databricks/cli/libs/cmdctx"
"github.com/databricks/cli/libs/databrickscfg/cfgpickers"
"github.com/databricks/cli/libs/flags"
"github.com/databricks/cli/libs/sqlexec"
"github.com/databricks/databricks-sdk-go"
"github.com/spf13/cobra"
)

const defaultLimit = 10

func showCommand() *cobra.Command {
var warehouseID string
var limit int

cmd := &cobra.Command{
Use: "show TABLE",
Short: "Preview a table's columns and sample rows",
Long: `Preview a table's columns and sample rows on a SQL warehouse.

TABLE is a fully-qualified name: catalog.schema.table (Unity Catalog) or
schema.table (legacy Hive metastore).`,
Args: root.ExactArgs(1),
PreRunE: root.MustWorkspaceClient,
RunE: func(cmd *cobra.Command, args []string) error {
// The CLI root doesn't cancel ctx on signals, so Ctrl-C / kill would
// otherwise leave Execute polling the warehouse until the query ends.
ctx, stop := signal.NotifyContext(cmd.Context(), os.Interrupt, syscall.SIGTERM)
defer stop()
w := cmdctx.WorkspaceClient(ctx)

if limit <= 0 {
return errors.New("--limit must be a positive integer")
}

quoted, err := quoteTableName(args[0])
if err != nil {
return err
}

warehouseID, err = resolveWarehouseID(ctx, w, warehouseID)
if err != nil {
return err
}

client := sqlexec.New(w.StatementExecution, warehouseID)
statement := fmt.Sprintf("SELECT * FROM %s LIMIT %d", quoted, limit)
result, err := client.Execute(ctx, statement)
if err != nil {
if ctx.Err() != nil {
fmt.Fprintln(cmd.ErrOrStderr(), "\nCancelled.")
return root.ErrAlreadyPrinted
}
return err
}

return render(ctx, cmd, result.Columns, result.Rows)
},
}

cmd.Flags().StringVar(&warehouseID, "warehouse-id", "", "SQL warehouse to run the query on. Defaults to DATABRICKS_WAREHOUSE_ID or a workspace default.")
cmd.Flags().IntVarP(&limit, "limit", "n", defaultLimit, "Maximum number of rows to fetch.")

return cmd
}

// warehouse to query either --warehouse-id flag, DATABRICKS_WAREHOUSE_ID config,
// or the workspace default warehouse.
func resolveWarehouseID(ctx context.Context, w *databricks.WorkspaceClient, flag string) (string, error) {
if flag != "" {
return flag, nil
}
if w.Config.WarehouseID != "" {
return w.Config.WarehouseID, nil
}
warehouse, err := cfgpickers.GetDefaultWarehouse(ctx, w)
if errors.Is(err, cfgpickers.ErrNoCompatibleWarehouses) {
return "", errors.New("no SQL warehouse available; pass --warehouse-id or set DATABRICKS_WAREHOUSE_ID")
}
if err != nil {
return "", err
}
return warehouse.Id, nil
}

// writes the result as JSON (--output json) or a terminal-width-aware table.
func render(ctx context.Context, cmd *cobra.Command, columns []string, rows [][]string) error {
out := cmd.OutOrStdout()
switch root.OutputType(cmd) {
case flags.OutputJSON:
return renderJSON(out, columns, rows)
case flags.OutputText:
if len(columns) == 0 {
return nil
}
return renderTable(ctx, columns, rows, detectWidth(out))
default:
return fmt.Errorf("unknown output type %s", root.OutputType(cmd))
}
}
25 changes: 25 additions & 0 deletions cmd/pipelines/show_identifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package pipelines

import (
"fmt"
"strings"
)

// validates a fully-qualified table name and returns it with each
// segment backtick-quoted. Accepts two-part (schema.table, Hive)
// and three-part (catalog.schema.table, Unity Catalog) names.
func quoteTableName(name string) (string, error) {
parts := strings.Split(name, ".")
if len(parts) < 2 || len(parts) > 3 {
return "", fmt.Errorf("invalid table name %q: expected catalog.schema.table (Unity Catalog) or schema.table (legacy Hive metastore)", name)
}

quoted := make([]string, len(parts))
for i, part := range parts {
if part == "" {
return "", fmt.Errorf("invalid table name %q: empty identifier segment", name)
}
quoted[i] = "`" + strings.ReplaceAll(part, "`", "``") + "`"
}
return strings.Join(quoted, "."), nil
}
Loading
Loading