From 670ea826599eea41518acdf3acca0f84a4a1ca0d Mon Sep 17 00:00:00 2001 From: Kenneth Shaw Date: Wed, 17 Nov 2021 07:38:28 +0700 Subject: [PATCH] Cleaning up errors from metadata reader/writer --- drivers/completer/completer.go | 3 +- drivers/drivers.go | 8 ++-- .../metadata/informationschema/metadata.go | 16 ++++---- drivers/metadata/metadata.go | 24 +++++------ drivers/metadata/reader.go | 28 +++++++------ drivers/metadata/writer.go | 40 +++++++++---------- metacmd/cmds.go | 16 ++++---- text/errors.go | 4 ++ text/text.go | 2 +- 9 files changed, 72 insertions(+), 69 deletions(-) diff --git a/drivers/completer/completer.go b/drivers/completer/completer.go index 5c93a758d9f..942d990d33c 100644 --- a/drivers/completer/completer.go +++ b/drivers/completer/completer.go @@ -13,6 +13,7 @@ import ( "github.com/gohxs/readline" "github.com/xo/usql/drivers/metadata" "github.com/xo/usql/env" + "github.com/xo/usql/text" ) const ( @@ -856,7 +857,7 @@ func qualifiedIdentifier(filter metadata.Filter, catalog, schema, name string) s func (c completer) getNames(query func() (iterator, error), mapper func(interface{}) string) []string { res, err := query() if err != nil { - if err != metadata.ErrNotSupported { + if err != text.ErrNotSupported { c.logger.Println("Error getting selectables", err) } return nil diff --git a/drivers/drivers.go b/drivers/drivers.go index a063718b2ef..3d83a2f0250 100644 --- a/drivers/drivers.go +++ b/drivers/drivers.go @@ -449,7 +449,7 @@ func ForceQueryParameters(params []string) func(*dburl.URL) { func NewMetadataReader(ctx context.Context, u *dburl.URL, db DB, w io.Writer, opts ...metadata.ReaderOption) (metadata.Reader, error) { d, ok := drivers[u.Driver] if !ok || d.NewMetadataReader == nil { - return nil, fmt.Errorf(text.NotSupportedByDriver, `describe commands`) + return nil, fmt.Errorf(text.NotSupportedByDriver, `describe commands`, u.Driver) } return d.NewMetadataReader(db, opts...), nil } @@ -458,13 +458,13 @@ func NewMetadataReader(ctx context.Context, u *dburl.URL, db DB, w io.Writer, op func NewMetadataWriter(ctx context.Context, u *dburl.URL, db DB, w io.Writer, opts ...metadata.ReaderOption) (metadata.Writer, error) { d, ok := drivers[u.Driver] if !ok { - return nil, fmt.Errorf(text.NotSupportedByDriver, `describe commands`) + return nil, fmt.Errorf(text.NotSupportedByDriver, `describe commands`, u.Driver) } if d.NewMetadataWriter != nil { return d.NewMetadataWriter(db, w, opts...), nil } if d.NewMetadataReader == nil { - return nil, fmt.Errorf(text.NotSupportedByDriver, `describe commands`) + return nil, fmt.Errorf(text.NotSupportedByDriver, `describe commands`, u.Driver) } newMetadataWriter := metadata.NewDefaultWriter(d.NewMetadataReader(db, opts...)) return newMetadataWriter(db, w), nil @@ -503,7 +503,7 @@ func Copy(ctx context.Context, u *dburl.URL, stdout, stderr func() io.Writer, ro return 0, WrapErr(u.Driver, text.ErrDriverNotAvailable) } if d.Copy == nil { - return 0, fmt.Errorf(text.NotSupportedByDriver, "copy") + return 0, fmt.Errorf(text.NotSupportedByDriver, "copy", u.Driver) } db, err := Open(u, stdout, stderr) if err != nil { diff --git a/drivers/metadata/informationschema/metadata.go b/drivers/metadata/informationschema/metadata.go index 2f5fe745b1c..0df15474814 100644 --- a/drivers/metadata/informationschema/metadata.go +++ b/drivers/metadata/informationschema/metadata.go @@ -10,6 +10,7 @@ import ( "github.com/xo/usql/drivers" "github.com/xo/usql/drivers/metadata" + "github.com/xo/usql/text" ) // InformationSchema metadata reader @@ -331,7 +332,7 @@ FROM information_schema.schemata // Functions from selected catalog (or all, if empty), matching schemas, names and types func (s InformationSchema) Functions(f metadata.Filter) (*metadata.FunctionSet, error) { if !s.hasFunctions { - return nil, metadata.ErrNotSupported + return nil, text.ErrNotSupported } columns := []string{ @@ -393,7 +394,7 @@ func (s InformationSchema) Functions(f metadata.Filter) (*metadata.FunctionSet, // FunctionColumns (arguments) from selected catalog (or all, if empty), matching schemas and functions func (s InformationSchema) FunctionColumns(f metadata.Filter) (*metadata.FunctionColumnSet, error) { if !s.hasFunctions { - return nil, metadata.ErrNotSupported + return nil, text.ErrNotSupported } columns := []string{ @@ -457,7 +458,7 @@ func (s InformationSchema) FunctionColumns(f metadata.Filter) (*metadata.Functio // Indexes from selected catalog (or all, if empty), matching schemas and names func (s InformationSchema) Indexes(f metadata.Filter) (*metadata.IndexSet, error) { if !s.hasIndexes { - return nil, metadata.ErrNotSupported + return nil, text.ErrNotSupported } qstr := `SELECT @@ -512,7 +513,7 @@ GROUP BY table_catalog, index_schema, table_name, index_name, // IndexColumns from selected catalog (or all, if empty), matching schemas and indexes func (s InformationSchema) IndexColumns(f metadata.Filter) (*metadata.IndexColumnSet, error) { if !s.hasIndexes { - return nil, metadata.ErrNotSupported + return nil, text.ErrNotSupported } qstr := `SELECT @@ -565,7 +566,7 @@ JOIN information_schema.columns c ON // Constraintes from selected catalog (or all, if empty), matching schemas and names func (s InformationSchema) Constraints(f metadata.Filter) (*metadata.ConstraintSet, error) { if !s.hasConstraints { - return nil, metadata.ErrNotSupported + return nil, text.ErrNotSupported } columns := []string{ @@ -654,7 +655,7 @@ LEFT JOIN information_schema.check_constraints c ON t.constraint_catalog = c.con // ConstraintColumns from selected catalog (or all, if empty), matching schemas and constraints func (s InformationSchema) ConstraintColumns(f metadata.Filter) (*metadata.ConstraintColumnSet, error) { if !s.hasConstraints { - return nil, metadata.ErrNotSupported + return nil, text.ErrNotSupported } vals := []interface{}{} @@ -761,9 +762,8 @@ LEFT JOIN information_schema.key_column_usage f ON r.unique_constraint_catalog = // Sequences from selected catalog (or all, if empty), matching schemas and names func (s InformationSchema) Sequences(f metadata.Filter) (*metadata.SequenceSet, error) { if !s.hasSequences { - return nil, metadata.ErrNotSupported + return nil, text.ErrNotSupported } - columns := []string{ "sequence_catalog", "sequence_schema", diff --git a/drivers/metadata/metadata.go b/drivers/metadata/metadata.go index 7e855a75e8d..79763cb5fb1 100644 --- a/drivers/metadata/metadata.go +++ b/drivers/metadata/metadata.go @@ -1,12 +1,8 @@ package metadata import ( - "errors" -) - -var ( - ErrNotSupported = errors.New("error: not supported") - ErrScanArgsCount = errors.New("error: wrong number of arguments for Scan()") + "github.com/xo/dburl" + "github.com/xo/usql/text" ) // ExtendedReader of all database metadata in a structured format. @@ -140,19 +136,19 @@ type Filter struct { // Writer of database metadata in a human readable format. type Writer interface { // DescribeFunctions \df, \dfa, \dfn, \dft, \dfw, etc. - DescribeFunctions(string, string, bool, bool) error + DescribeFunctions(*dburl.URL, string, string, bool, bool) error // DescribeTableDetails \d foo - DescribeTableDetails(string, bool, bool) error + DescribeTableDetails(*dburl.URL, string, bool, bool) error // ListAllDbs \l - ListAllDbs(string, bool) error + ListAllDbs(*dburl.URL, string, bool) error // ListTables \dt, \dv, \dm, etc. - ListTables(string, string, bool, bool) error + ListTables(*dburl.URL, string, string, bool, bool) error // ListSchemas \dn - ListSchemas(string, bool, bool) error + ListSchemas(*dburl.URL, string, bool, bool) error // ListIndexes \di - ListIndexes(string, bool, bool) error + ListIndexes(*dburl.URL, string, bool, bool) error // ShowStats \ss - ShowStats(string, string, bool, int) error + ShowStats(*dburl.URL, string, string, bool, int) error } type CatalogSet struct { @@ -898,7 +894,7 @@ func (r resultSet) Scan(dest ...interface{}) error { v = r.scanValues(r.results[r.current-1]) } if len(v) != len(dest) { - return ErrScanArgsCount + return text.ErrWrongNumberOfArguments } for i, d := range dest { p := d.(*interface{}) diff --git a/drivers/metadata/reader.go b/drivers/metadata/reader.go index d89d1018442..3a3d04fbe2d 100644 --- a/drivers/metadata/reader.go +++ b/drivers/metadata/reader.go @@ -4,6 +4,8 @@ import ( "context" "database/sql" "time" + + "github.com/xo/usql/text" ) // PluginReader allows to be easily composed from other readers @@ -74,91 +76,91 @@ func NewPluginReader(readers ...Reader) Reader { func (p PluginReader) Catalogs(f Filter) (*CatalogSet, error) { if p.catalogs == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.catalogs(f) } func (p PluginReader) Schemas(f Filter) (*SchemaSet, error) { if p.schemas == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.schemas(f) } func (p PluginReader) Tables(f Filter) (*TableSet, error) { if p.tables == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.tables(f) } func (p PluginReader) Columns(f Filter) (*ColumnSet, error) { if p.columns == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.columns(f) } func (p PluginReader) ColumnStats(f Filter) (*ColumnStatSet, error) { if p.columnStats == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.columnStats(f) } func (p PluginReader) Indexes(f Filter) (*IndexSet, error) { if p.indexes == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.indexes(f) } func (p PluginReader) IndexColumns(f Filter) (*IndexColumnSet, error) { if p.indexColumns == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.indexColumns(f) } func (p PluginReader) Triggers(f Filter) (*TriggerSet, error) { if p.triggers == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.triggers(f) } func (p PluginReader) Constraints(f Filter) (*ConstraintSet, error) { if p.constraints == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.constraints(f) } func (p PluginReader) ConstraintColumns(f Filter) (*ConstraintColumnSet, error) { if p.constraintColumns == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.constraintColumns(f) } func (p PluginReader) Functions(f Filter) (*FunctionSet, error) { if p.functions == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.functions(f) } func (p PluginReader) FunctionColumns(f Filter) (*FunctionColumnSet, error) { if p.functionColumns == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.functionColumns(f) } func (p PluginReader) Sequences(f Filter) (*SequenceSet, error) { if p.sequences == nil { - return nil, ErrNotSupported + return nil, text.ErrNotSupported } return p.sequences(f) } diff --git a/drivers/metadata/writer.go b/drivers/metadata/writer.go index a803eaf9f30..6738ce31b6b 100644 --- a/drivers/metadata/writer.go +++ b/drivers/metadata/writer.go @@ -7,6 +7,7 @@ import ( "io" "strings" + "github.com/xo/dburl" "github.com/xo/tblfmt" "github.com/xo/usql/env" "github.com/xo/usql/text" @@ -38,8 +39,6 @@ type DefaultWriter struct { listAllDbs func(string, bool) error } -var _ Writer = &DefaultWriter{} - func NewDefaultWriter(r Reader, opts ...WriterOption) func(db DB, w io.Writer) Writer { defaultWriter := &DefaultWriter{ r: r, @@ -91,10 +90,10 @@ func WithListAllDbs(f func(string, bool) error) WriterOption { } // DescribeFunctions matching pattern -func (w DefaultWriter) DescribeFunctions(funcTypes, pattern string, verbose, showSystem bool) error { +func (w DefaultWriter) DescribeFunctions(u *dburl.URL, funcTypes, pattern string, verbose, showSystem bool) error { r, ok := w.r.(FunctionReader) if !ok { - return fmt.Errorf(text.NotSupportedByDriver, `\df`) + return fmt.Errorf(text.NotSupportedByDriver, `\df`, u.Driver) } types := []string{} for k, v := range w.funcTypes { @@ -176,7 +175,7 @@ func (w DefaultWriter) getFunctionColumns(c, s, f string) (string, error) { } // DescribeTableDetails matching pattern -func (w DefaultWriter) DescribeTableDetails(pattern string, verbose, showSystem bool) error { +func (w DefaultWriter) DescribeTableDetails(u *dburl.URL, pattern string, verbose, showSystem bool) error { sp, tp, err := parsePattern(pattern) if err != nil { return fmt.Errorf("failed to parse search pattern: %w", err) @@ -221,7 +220,7 @@ func (w DefaultWriter) DescribeTableDetails(pattern string, verbose, showSystem _, isICR := w.r.(IndexColumnReader) if isIR && isICR { res, err := ir.Indexes(Filter{Schema: sp, Name: tp, WithSystem: showSystem}) - if err != nil && err != ErrNotSupported { + if err != nil && err != text.ErrNotSupported { return fmt.Errorf("failed to list indexes for table %s: %w", tp, err) } if res != nil { @@ -366,13 +365,14 @@ func (w DefaultWriter) tableDetailsSummary(sp, tp string) func(io.Writer, int) ( return 0, err } } + func (w DefaultWriter) describeTableTriggers(out io.Writer, sp, tp string) error { r, ok := w.r.(TriggerReader) if !ok { return nil } res, err := r.Triggers(Filter{Schema: sp, Parent: tp}) - if err != nil && err != ErrNotSupported { + if err != nil && err != text.ErrNotSupported { return fmt.Errorf("failed to list triggers for table %s: %w", tp, err) } if res == nil { @@ -397,7 +397,7 @@ func (w DefaultWriter) describeTableIndexes(out io.Writer, sp, tp string) error return nil } res, err := r.Indexes(Filter{Schema: sp, Parent: tp}) - if err != nil && err != ErrNotSupported { + if err != nil && err != text.ErrNotSupported { return fmt.Errorf("failed to list indexes for table %s: %w", tp, err) } if res == nil { @@ -447,7 +447,7 @@ func (w DefaultWriter) describeTableConstraints(out io.Writer, filter Filter, po return nil } res, err := r.Constraints(filter) - if err != nil && err != ErrNotSupported { + if err != nil && err != text.ErrNotSupported { return fmt.Errorf("failed to list constraints: %w", err) } if res == nil { @@ -488,7 +488,7 @@ func (w DefaultWriter) getConstraintColumns(c, s, t, n string) (string, string, func (w DefaultWriter) describeSequences(sp, tp string, verbose, showSystem bool) (int, error) { r := w.r.(SequenceReader) res, err := r.Sequences(Filter{Schema: sp, Name: tp, WithSystem: showSystem}) - if err != nil && err != ErrNotSupported { + if err != nil && err != text.ErrNotSupported { return 0, err } if res == nil { @@ -545,13 +545,13 @@ func (w DefaultWriter) describeIndex(i *Index) error { } // ListAllDbs matching pattern -func (w DefaultWriter) ListAllDbs(pattern string, verbose bool) error { +func (w DefaultWriter) ListAllDbs(u *dburl.URL, pattern string, verbose bool) error { if w.listAllDbs != nil { return w.listAllDbs(pattern, verbose) } r, ok := w.r.(CatalogReader) if !ok { - return fmt.Errorf(text.NotSupportedByDriver, `\l`) + return fmt.Errorf(text.NotSupportedByDriver, `\l`, u.Driver) } res, err := r.Catalogs(Filter{Name: pattern}) if err != nil { @@ -565,10 +565,10 @@ func (w DefaultWriter) ListAllDbs(pattern string, verbose bool) error { } // ListTables matching pattern -func (w DefaultWriter) ListTables(tableTypes, pattern string, verbose, showSystem bool) error { +func (w DefaultWriter) ListTables(u *dburl.URL, tableTypes, pattern string, verbose, showSystem bool) error { r, ok := w.r.(TableReader) if !ok { - return fmt.Errorf(text.NotSupportedByDriver, `\dt`) + return fmt.Errorf(text.NotSupportedByDriver, `\dt`, u.Driver) } types := []string{} for k, v := range w.tableTypes { @@ -617,10 +617,10 @@ func (w DefaultWriter) ListTables(tableTypes, pattern string, verbose, showSyste } // ListSchemas matching pattern -func (w DefaultWriter) ListSchemas(pattern string, verbose, showSystem bool) error { +func (w DefaultWriter) ListSchemas(u *dburl.URL, pattern string, verbose, showSystem bool) error { r, ok := w.r.(SchemaReader) if !ok { - return fmt.Errorf(text.NotSupportedByDriver, `\d`) + return fmt.Errorf(text.NotSupportedByDriver, `\d`, u.Driver) } res, err := r.Schemas(Filter{Name: pattern, WithSystem: showSystem}) if err != nil { @@ -641,10 +641,10 @@ func (w DefaultWriter) ListSchemas(pattern string, verbose, showSystem bool) err } // ListIndexes matching pattern -func (w DefaultWriter) ListIndexes(pattern string, verbose, showSystem bool) error { +func (w DefaultWriter) ListIndexes(u *dburl.URL, pattern string, verbose, showSystem bool) error { r, ok := w.r.(IndexReader) if !ok { - return fmt.Errorf(text.NotSupportedByDriver, `\di`) + return fmt.Errorf(text.NotSupportedByDriver, `\di`, u.Driver) } sp, tp, err := parsePattern(pattern) if err != nil { @@ -689,10 +689,10 @@ func (w DefaultWriter) ListIndexes(pattern string, verbose, showSystem bool) err } // ShowStats of columns for tables matching pattern -func (w DefaultWriter) ShowStats(statTypes, pattern string, verbose bool, k int) error { +func (w DefaultWriter) ShowStats(u *dburl.URL, statTypes, pattern string, verbose bool, k int) error { r, ok := w.r.(ColumnStatReader) if !ok { - return fmt.Errorf(text.NotSupportedByDriver, `\ss`) + return fmt.Errorf(text.NotSupportedByDriver, `\ss`, u.Driver) } sp, tp, err := parsePattern(pattern) if err != nil { diff --git a/metacmd/cmds.go b/metacmd/cmds.go index 1f5b52fa6c6..ecb2f76f3e5 100644 --- a/metacmd/cmds.go +++ b/metacmd/cmds.go @@ -752,19 +752,19 @@ func init() { switch name { case "d": if pattern != "" { - return m.DescribeTableDetails(pattern, verbose, showSystem) + return m.DescribeTableDetails(p.Handler.URL(), pattern, verbose, showSystem) } - return m.ListTables("tvmsE", pattern, verbose, showSystem) + return m.ListTables(p.Handler.URL(), "tvmsE", pattern, verbose, showSystem) case "df", "da": - return m.DescribeFunctions(name, pattern, verbose, showSystem) + return m.DescribeFunctions(p.Handler.URL(), name, pattern, verbose, showSystem) case "dt", "dtv", "dtm", "dts", "dv", "dm", "ds": - return m.ListTables(name, pattern, verbose, showSystem) + return m.ListTables(p.Handler.URL(), name, pattern, verbose, showSystem) case "dn": - return m.ListSchemas(pattern, verbose, showSystem) + return m.ListSchemas(p.Handler.URL(), pattern, verbose, showSystem) case "di": - return m.ListIndexes(pattern, verbose, showSystem) + return m.ListIndexes(p.Handler.URL(), pattern, verbose, showSystem) case "l": - return m.ListAllDbs(pattern, verbose) + return m.ListAllDbs(p.Handler.URL(), pattern, verbose) } return nil }, @@ -804,7 +804,7 @@ func init() { return err } } - return m.ShowStats(name, pattern, verbose, k) + return m.ShowStats(p.Handler.URL(), name, pattern, verbose, k) }, }, Copy: { diff --git a/text/errors.go b/text/errors.go index 173122cf3a1..251b2b04506 100644 --- a/text/errors.go +++ b/text/errors.go @@ -63,4 +63,8 @@ var ( ErrUnableToNormalizeURL = errors.New("unable to normalize URL") // ErrInvalidIsolationLevel is the invalid isolation level error. ErrInvalidIsolationLevel = errors.New("invalid isolation level") + // ErrNotSupported is the not supported error. + ErrNotSupported = errors.New("not supported") + // ErrWrongNumberOfArguments is the wrong number of arguments error. + ErrWrongNumberOfArguments = errors.New("wrong number of arguments") ) diff --git a/text/text.go b/text/text.go index d62d244bf1d..47d3bc720fc 100644 --- a/text/text.go +++ b/text/text.go @@ -81,7 +81,7 @@ var ( TimingSet = `Timing is %s.` TimingDesc = `Time: %0.3f ms` InvalidValue = `invalid -%s value %q: %s` - NotSupportedByDriver = `%s not supported by driver` + NotSupportedByDriver = `%s not supported by %s driver` RelationNotFound = `Did not find any relation named "%s".` InvalidOption = `invalid option %q` NotificationReceived = `Asynchronous notification %q %sreceived from server process with PID %d.`