diff --git a/drivers/metadata/informationschema/metadata.go b/drivers/metadata/informationschema/metadata.go index 54ab63abb10..6547ab16fa7 100644 --- a/drivers/metadata/informationschema/metadata.go +++ b/drivers/metadata/informationschema/metadata.go @@ -266,7 +266,7 @@ func (s InformationSchema) Columns(f metadata.Filter) (*metadata.ColumnSet, erro return metadata.NewColumnSet(results), nil } -// Tables from selected catalog (or all, if empty), matching schemas, names and types +// Tables, views, materialized views and sequences from selected catalog (or all, if empty), matching schemas, names and types func (s InformationSchema) Tables(f metadata.Filter) (*metadata.TableSet, error) { qstr := `SELECT table_catalog, diff --git a/drivers/metadata/metadata.go b/drivers/metadata/metadata.go index 7924ba72b54..efd7db80ebf 100644 --- a/drivers/metadata/metadata.go +++ b/drivers/metadata/metadata.go @@ -44,7 +44,7 @@ type SchemaReader interface { Schemas(Filter) (*SchemaSet, error) } -// TableReader lists database tables. +// TableReader lists database tables, views, materialized views and sequences. type TableReader interface { Reader Tables(Filter) (*TableSet, error) diff --git a/drivers/metadata/postgres/metadata.go b/drivers/metadata/postgres/metadata.go index f1280f08636..995f096d1f9 100644 --- a/drivers/metadata/postgres/metadata.go +++ b/drivers/metadata/postgres/metadata.go @@ -102,6 +102,7 @@ FROM pg_catalog.pg_database d` return metadata.NewCatalogSet(results), nil } +// Tables, views, materialized views and sequences from selected catalog (or all, if empty), matching schemas, names and types func (r metaReader) Tables(f metadata.Filter) (*metadata.TableSet, error) { qstr := `SELECT n.nspname as "Schema", c.relname as "Name", @@ -112,7 +113,7 @@ func (r metaReader) Tables(f metadata.Filter) (*metadata.TableSet, error) { FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace ` - conds := []string{"n.nspname !~ '^pg_toast' AND c.relkind != 'c'"} + conds := []string{"n.nspname !~ '^pg_toast' AND c.relkind IN ('r', 'p', 's', 'f', 'v', 'm', 'S')"} vals := []interface{}{} if f.OnlyVisible { conds = append(conds, "pg_catalog.pg_table_is_visible(c.oid)") diff --git a/drivers/metadata/postgres/metadata_test.go b/drivers/metadata/postgres/metadata_test.go index 05a08a55bcb..021634af881 100644 --- a/drivers/metadata/postgres/metadata_test.go +++ b/drivers/metadata/postgres/metadata_test.go @@ -125,6 +125,27 @@ func TestTriggers(t *testing.T) { } } +func TestTables(t *testing.T) { + schema := "public" + expected := "actor_actor_id_seq, address_address_id_seq, category_category_id_seq, city_city_id_seq, country_country_id_seq, customer_customer_id_seq, film_film_id_seq, inventory_inventory_id_seq, language_language_id_seq, payment_payment_id_seq, rental_rental_id_seq, staff_staff_id_seq, store_store_id_seq, actor, address, category, city, country, customer, film, film_actor, film_category, inventory, language, payment, payment_p2007_01, payment_p2007_02, payment_p2007_03, payment_p2007_04, payment_p2007_05, payment_p2007_06, rental, staff, store, actor_info, customer_list, film_list, nicer_but_slower_film_list, sales_by_film_category, sales_by_store, staff_list" + parent := "film" + r := postgres.NewReader()(db.DB).(metadata.TableReader) + + result, err := r.Tables(metadata.Filter{Schema: schema, Parent: parent}) + if err != nil { + log.Fatalf("Could not read %s triggers: %v", dbName, err) + } + + names := []string{} + for result.Next() { + names = append(names, result.Get().Name) + } + actual := strings.Join(names, ", ") + if actual != expected { + t.Errorf("Wrong %s table names, expected:\n %v\ngot:\n %v", dbName, expected, names) + } +} + func TestColumns(t *testing.T) { // Only testing postgres specific datatype formatting. // The rest of the functionality is covered by informationschema/metadata_test.go:TestColumns diff --git a/drivers/metadata/writer.go b/drivers/metadata/writer.go index db45af2a49d..9392e7b3784 100644 --- a/drivers/metadata/writer.go +++ b/drivers/metadata/writer.go @@ -95,12 +95,7 @@ func (w DefaultWriter) DescribeFunctions(u *dburl.URL, funcTypes, pattern string if !ok { return fmt.Errorf(text.NotSupportedByDriver, `\df`, u.Driver) } - types := []string{} - for k, v := range w.funcTypes { - if strings.ContainsRune(funcTypes, k) { - types = append(types, v...) - } - } + types := typesFromTypeRunes(funcTypes, w.funcTypes) sp, tp, err := parsePattern(pattern) if err != nil { return fmt.Errorf("failed to parse search pattern: %w", err) @@ -186,7 +181,8 @@ func (w DefaultWriter) DescribeTableDetails(u *dburl.URL, pattern string, verbos tr, isTR := w.r.(TableReader) _, isCR := w.r.(ColumnReader) if isTR && isCR { - res, err := tr.Tables(Filter{Schema: sp, Name: tp, WithSystem: showSystem}) + types := typesFromTypeRunes("tvm", w.tableTypes) + res, err := tr.Tables(Filter{Schema: sp, Name: tp, Types: types, WithSystem: showSystem}) if err != nil { return fmt.Errorf("failed to list tables: %w", err) } @@ -358,6 +354,9 @@ func (w DefaultWriter) tableDetailsSummary(sp, tp string) func(io.Writer, int) ( return err }, ) + if err != nil { + return 0, err + } err = w.describeTableTriggers(out, sp, tp) if err != nil { return 0, err @@ -505,6 +504,7 @@ func (w DefaultWriter) describeSequences(sp, tp string, verbose, showSystem bool params["footer"] = "off" params["title"] = fmt.Sprintf("Sequence \"%s.%s\"\n", s.Schema, s.Name) err = tblfmt.EncodeAll(w.w, rows, params) + w.w.Write([]byte("\n")) if err != nil { return 0, err } @@ -539,7 +539,7 @@ func (w DefaultWriter) describeIndex(i *Index) error { if i.IsPrimary == YES { primary = "primary key, " } - _, err := fmt.Fprintf(out, "%s%s, for table %s", primary, i.Type, i.Table) + _, err := fmt.Fprintf(out, "%s%s, for table %s\n", primary, i.Type, i.Table) return 0, err }) } @@ -570,12 +570,7 @@ func (w DefaultWriter) ListTables(u *dburl.URL, tableTypes, pattern string, verb if !ok { return fmt.Errorf(text.NotSupportedByDriver, `\dt`, u.Driver) } - types := []string{} - for k, v := range w.tableTypes { - if strings.ContainsRune(tableTypes, k) { - types = append(types, v...) - } - } + types := typesFromTypeRunes(tableTypes, w.tableTypes) sp, tp, err := parsePattern(pattern) if err != nil { return fmt.Errorf("failed to parse search pattern: %w", err) @@ -783,13 +778,7 @@ func (w DefaultWriter) ListPrivilegeSummaries(u *dburl.URL, pattern string, show return fmt.Errorf("failed to parse search pattern: %w", err) } // filter for tables, views and sequences - const tableTypes = "tvms" - types := []string{} - for k, v := range w.tableTypes { - if strings.ContainsRune(tableTypes, k) { - types = append(types, v...) - } - } + types := typesFromTypeRunes("tvms", w.tableTypes) res, err := r.PrivilegeSummaries(Filter{Schema: sp, Name: tp, WithSystem: showSystem, Types: types}) if err != nil { return fmt.Errorf("failed to list table privileges: %w", err) @@ -836,3 +825,13 @@ func qualifiedIdentifier(schema, name string) string { } return fmt.Sprintf("\"%s.%s\"", schema, name) } + +func typesFromTypeRunes(typeRunes string, mapping map[rune][]string) []string { + types := []string{} + for k, v := range mapping { + if strings.ContainsRune(typeRunes, k) { + types = append(types, v...) + } + } + return types +} diff --git a/drivers/testdata/pgsql.descTable.expected.txt b/drivers/testdata/pgsql.descTable.expected.txt index f36cddb7a89..55546c76156 100644 --- a/drivers/testdata/pgsql.descTable.expected.txt +++ b/drivers/testdata/pgsql.descTable.expected.txt @@ -103,3 +103,4 @@ Index "public.film_pkey" ---------+--------- film_id | integer primary key, index, for table film +