From 01ff8b318f3dafc14c7b79e34dd3d31423579702 Mon Sep 17 00:00:00 2001 From: thisisaaronland Date: Wed, 11 Aug 2021 10:12:47 -0700 Subject: [PATCH] add query package and tool --- cmd/query/main.go | 50 +++++++++++++++++++++++++++++++++ database/database.go | 8 +++--- query/csvwriter.go | 35 +++++++++++++++++++++++ query/writer.go | 66 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 cmd/query/main.go create mode 100644 query/csvwriter.go create mode 100644 query/writer.go diff --git a/cmd/query/main.go b/cmd/query/main.go new file mode 100644 index 0000000..be38b2f --- /dev/null +++ b/cmd/query/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "context" + "flag" + "github.com/aaronland/go-sqlite/database" + "github.com/aaronland/go-sqlite/query" + "log" + "os" +) + +func main() { + + dsn := flag.String("dsn", "", "A valid SQLite DSN") + query_str := flag.String("query", "", "A valid SQL query") + + flag.Parse() + + ctx := context.Background() + + db, err := database.NewDB(ctx, *dsn) + + if err != nil { + log.Fatalf("Failed to create new database, %v", err) + } + + conn, err := db.Conn() + + if err != nil { + log.Fatalf("Failed to get database connection, %v", err) + } + + rows, err := conn.Query(*query_str) + + if err != nil { + log.Fatalf("query err: %s", err) + } + + wr, err := query.NewCSVQueryWriter(ctx, os.Stdout) + + if err != nil { + log.Fatalf("Failed to create query writer, %v", err) + } + + err = query.WriteRows(ctx, wr, rows) + + if err != nil { + log.Fatalf("Failed to write rows, %v", err) + } +} diff --git a/database/database.go b/database/database.go index 02753b6..4f9d36b 100644 --- a/database/database.go +++ b/database/database.go @@ -5,11 +5,11 @@ import ( "database/sql" "fmt" _ "github.com/mattn/go-sqlite3" + "github.com/psanford/sqlite3vfs" + "github.com/psanford/sqlite3vfshttp" _ "log" "strings" "sync" - "github.com/psanford/sqlite3vfshttp" - "github.com/psanford/sqlite3vfs" ) type SQLiteDatabase struct { @@ -43,13 +43,13 @@ func NewDBWithDriver(ctx context.Context, driver string, dsn string) (*SQLiteDat } err := sqlite3vfs.RegisterVFS("httpvfs", &vfs) - + if err != nil { return nil, err } dsn = "not_a_real_name.db?vfs=httpvfs&mode=ro" - + } else { // https://github.com/mattn/go-sqlite3/issues/39 diff --git a/query/csvwriter.go b/query/csvwriter.go new file mode 100644 index 0000000..1bc09dd --- /dev/null +++ b/query/csvwriter.go @@ -0,0 +1,35 @@ +package query + +import ( + "context" + "encoding/csv" + "io" +) + +type CSVQueryWriter struct { + QueryWriter + csv_writer *csv.Writer +} + +func NewCSVQueryWriter(ctx context.Context, wr io.Writer) (QueryWriter, error) { + + csv_wr := csv.NewWriter(wr) + + query_wr := &CSVQueryWriter{ + csv_writer: csv_wr, + } + + return query_wr, nil +} + +func (query_wr *CSVQueryWriter) WriteRow(ctx context.Context, row []string) error { + + err := query_wr.csv_writer.Write(row) + + if err != nil { + return err + } + + query_wr.csv_writer.Flush() + return nil +} diff --git a/query/writer.go b/query/writer.go new file mode 100644 index 0000000..486733a --- /dev/null +++ b/query/writer.go @@ -0,0 +1,66 @@ +package query + +import ( + "context" + "database/sql" + "fmt" +) + +type QueryWriter interface { + WriteRow(context.Context, []string) error +} + +// The guts of this (minus the QueryWriter stuff) are copied from: +// https://github.com/psanford/sqlite3vfshttp/blob/main/sqlitehttpcli/sqlitehttpcli.go + +func WriteRows(ctx context.Context, wr QueryWriter, rows *sql.Rows) error { + + cols, err := rows.Columns() + + if err != nil { + return fmt.Errorf("Failed to determine columns for rows, %v", err) + } + + for rows.Next() { + + rows.Columns() + + columns := make([]*string, len(cols)) + + columnPointers := make([]interface{}, len(cols)) + + for i := range columns { + columnPointers[i] = &columns[i] + } + + err = rows.Scan(columnPointers...) + + if err != nil { + return fmt.Errorf("Failed to scan row, %w", err) + } + + names := make([]string, 0, len(columns)) + + for _, col := range columns { + if col == nil { + names = append(names, "NULL") + } else { + names = append(names, *col) + } + } + + err = wr.WriteRow(ctx, names) + + if err != nil { + return fmt.Errorf("Failed to write row, %v", err) + } + } + + err = rows.Close() + + if err != nil { + return fmt.Errorf("Failed to close rows, %v", err) + } + + return nil +}