From f5f7c5e551b046d8e5022ea2770935e30e2215b1 Mon Sep 17 00:00:00 2001 From: Travis Turner Date: Tue, 21 Mar 2023 21:10:31 -0500 Subject: [PATCH] fbsql: add support for `\d` meta-command. (#2340) `\d` will list tables (in the future it will also include things like views) `\d tablename` will show info about tablename --- cli/cli_integration_test.go | 1 + cli/meta.go | 49 ++++++++++++++++++++++++++++++++++--- cli/testdata/meta_describe | 33 +++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 cli/testdata/meta_describe diff --git a/cli/cli_integration_test.go b/cli/cli_integration_test.go index 1ca7966ec..bdc47db1f 100644 --- a/cli/cli_integration_test.go +++ b/cli/cli_integration_test.go @@ -66,6 +66,7 @@ func TestCLIIntegration(t *testing.T) { "meta_bang", "meta_cd", "meta_echo", + "meta_describe", "meta_file", "meta_pset_border", "meta_pset_expanded", diff --git a/cli/meta.go b/cli/meta.go index a06d7b713..74d1816c7 100644 --- a/cli/meta.go +++ b/cli/meta.go @@ -2,6 +2,7 @@ package cli import ( "bufio" + "fmt" "io" "os" "os/exec" @@ -39,6 +40,7 @@ var _ metaCommand = (*metaBang)(nil) var _ metaCommand = (*metaBorder)(nil) var _ metaCommand = (*metaChangeDirectory)(nil) var _ metaCommand = (*metaConnect)(nil) +var _ metaCommand = (*metaDescribe)(nil) var _ metaCommand = (*metaEcho)(nil) var _ metaCommand = (*metaExpanded)(nil) var _ metaCommand = (*metaFile)(nil) @@ -363,6 +365,7 @@ Input/Output Informational \d list tables + \d NAME describe table \dt list tables \dv list views \l[ist] list databases @@ -375,6 +378,7 @@ Formatting Connection \c[onnect] [DBNAME] connect to new database + disconnect by sending DBNAME "-" \org [ORGNAME] set organization id Operating System @@ -484,7 +488,44 @@ func (m *metaListDatabases) execute(cmd *Command) (responseAction, error) { } // //////////////////////////////////////////////////////////////////////////// -// list tables (d or dt) +// describe (d) +// //////////////////////////////////////////////////////////////////////////// +type metaDescribe struct { + args []string +} + +func newMetaDescribe(args []string) *metaDescribe { + return &metaDescribe{ + args: args, + } +} + +func (m *metaDescribe) execute(cmd *Command) (responseAction, error) { + switch len(m.args) { + case 0: + // Describe with no args should list all relations (tables, views, + // etc.). For now, we're just going to list the tables. + return newMetaListTables().execute(cmd) + + case 1: + // Describe with a single arg will assume the arg is a table name, so it + // runs a `SHOW COLUMNS` for that table. + qry := []queryPart{ + newPartRaw(fmt.Sprintf(`SHOW COLUMNS FROM "%s"`, m.args[0])), + } + + if err := cmd.executeAndWriteQuery(qry); err != nil { + return actionNone, errors.Wrap(err, "executing query") + } + + return actionReset, nil + default: + return actionNone, errors.Errorf("meta command 'describe' takes zero or one argument") + } +} + +// //////////////////////////////////////////////////////////////////////////// +// describe (dt) // //////////////////////////////////////////////////////////////////////////// type metaListTables struct{} @@ -505,7 +546,7 @@ func (m *metaListTables) execute(cmd *Command) (responseAction, error) { } // //////////////////////////////////////////////////////////////////////////// -// list views (dv) +// describe views (dv) // //////////////////////////////////////////////////////////////////////////// type metaListViews struct{} @@ -1064,7 +1105,9 @@ func splitMetaCommand(in string, replacer *replacer) (metaCommand, error) { return newMetaChangeDirectory(args), nil case "c", "connect": return newMetaConnect(args), nil - case "d", "dt": + case "d": + return newMetaDescribe(args), nil + case "dt": return newMetaListTables(), nil case "dv": return newMetaListViews(), nil diff --git a/cli/testdata/meta_describe b/cli/testdata/meta_describe new file mode 100644 index 000000000..aada2c361 --- /dev/null +++ b/cli/testdata/meta_describe @@ -0,0 +1,33 @@ +// TODO(tlt): we can't run this test until we get the system tables under control (i.e. sorted). Currently, fb_views is in a map with users, so the following can fail 50% of the time. +// Show tables for database by calling describe with no args. +// SEND:\d +// EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+------------------------+ +// EXPECT:| _id | name | owner | updated_by | created_at | updated_at | keys | space_used | description | +// EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+------------------------+ +// EXPECTCOMP:WithFormat:| fb_veiws | fb_views | | | {timestamp} | {timestamp} | true | 0 | system table for views | +// EXPECTCOMP:WithFormat:| users | users | | | {timestamp} | {timestamp} | false | 0 | | +// EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | | +// EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | | +// EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | | +// EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | | +// EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | | +// EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+------------------------+ +// EXPECT: + +// Show columns for table. +SEND:\d users +EXPECT:+------+------+--------+----------------------+-------+------------+------------+-------+----------------------+---------------------+----------+-------+-------------+-----+ +EXPECT:| _id | name | type | created_at | keys | cache_type | cache_size | scale | min | max | timeunit | epoch | timequantum | ttl | +EXPECT:+------+------+--------+----------------------+-------+------------+------------+-------+----------------------+---------------------+----------+-------+-------------+-----+ +EXPECTCOMP:WithFormat:| _id | _id | id | {timestamp} | false | | 0 | 0 | 0 | 0 | | 0 | | 0s | +EXPECTCOMP:WithFormat:| name | name | string | {timestamp} | true | ranked | 50000 | 0 | 0 | 0 | | 0 | | 0s | +EXPECTCOMP:WithFormat:| age | age | int | {timestamp} | false | | 0 | 0 | -9223372036854775808 | 9223372036854775807 | | 0 | | 0s | +EXPECT:+------+------+--------+----------------------+-------+------------+------------+-------+----------------------+---------------------+----------+-------+-------------+-----+ +EXPECT: + +// Show columns for an invalid table. +SEND:\d invalid +EXPECT:Error: compiling plan: [1:19] table 'invalid' not found + +SEND:\d users extra +EXPECT:executing meta command: meta command 'describe' takes zero or one argument \ No newline at end of file