Skip to content

Commit

Permalink
Add .import command
Browse files Browse the repository at this point in the history
  • Loading branch information
asdine committed May 12, 2021
1 parent 07eed68 commit 91580d6
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 28 deletions.
77 changes: 74 additions & 3 deletions cmd/genji/shell/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package shell

import (
"context"
"encoding/csv"
"errors"
"fmt"
"io"
"os"
"strings"

"github.com/genjidb/genji"
Expand All @@ -14,13 +17,19 @@ import (
"github.com/genjidb/genji/stringutil"
)

var commands = []struct {
type command struct {
Name string
Options string
DisplayName string
Description string
Aliases []string
}{
}

func (c *command) Usage() string {
return fmt.Sprintf("%s %s", c.DisplayName, c.Options)
}

var commands = []command{
{
Name: ".exit",
DisplayName: ".exit or exit",
Expand Down Expand Up @@ -52,7 +61,7 @@ var commands = []struct {
},
{
Name: ".save",
Options: "[badger?] [filename]",
Options: "[badger] [filename]",
DisplayName: ".save",
Description: "Save database content in the specified file.",
},
Expand All @@ -62,6 +71,22 @@ var commands = []struct {
DisplayName: ".schema",
Description: "Show the CREATE statements of all tables or of the selected ones.",
},
{
Name: ".import",
Options: "TYPE FILE table",
DisplayName: ".import",
Description: "Import data from a file. Only supported type is 'csv'",
},
}

func getUsage(cmdName string) string {
for _, c := range commands {
if c.Name == cmdName {
return c.Usage()
}
}

return ""
}

// runHelpCmd shows all available commands.
Expand Down Expand Up @@ -252,3 +277,49 @@ func runSaveCmd(ctx context.Context, db *genji.DB, engineName string, dbPath str

return nil
}

func runImportCmd(ctx context.Context, db *genji.DB, fileType, path, table string) error {
if strings.ToLower(fileType) != "csv" {
return errors.New("TYPE should be csv")
}

f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()

tx, err := db.Begin(true)
if err != nil {
return err
}
defer tx.Rollback()

r := csv.NewReader(f)

err = tx.Exec(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s", table))
if err != nil {
return err
}

headers, err := r.Read()
if err != nil {
return err
}

for {
columns, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
return err
}
err = tx.Exec("INSERT INTO "+table+" VALUES ?", document.NewFromCSV(headers, columns))
if err != nil {
return err
}
}

return tx.Commit()
}
35 changes: 10 additions & 25 deletions cmd/genji/shell/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"context"
"errors"
"fmt"
"io/ioutil"
"os"
"os/signal"
"path/filepath"
Expand Down Expand Up @@ -158,7 +157,7 @@ func Run(ctx context.Context, opts *Options) error {

err := sh.runPrompt(ctx, promptExecCh)
if err != nil && err != errExitCtrlD {
fmt.Fprintf(os.Stderr, err.Error())
fmt.Fprintln(os.Stderr, err.Error())
}
}()

Expand Down Expand Up @@ -446,19 +445,19 @@ func (sh *Shell) runCommand(ctx context.Context, in string) error {
return runHelpCmd()
case ".tables":
if len(cmd) > 1 {
return stringutil.Errorf("usage: .tables")
return stringutil.Errorf(getUsage(".tables"))
}

return runTablesCmd(sh.db, os.Stdout)
case ".exit", "exit":
if len(cmd) > 1 {
return stringutil.Errorf("usage: .exit")
return stringutil.Errorf(getUsage(".exit"))
}

return errExitCommand
case ".indexes":
if len(cmd) > 2 {
return stringutil.Errorf("usage: .indexes [tablename]")
return stringutil.Errorf(getUsage(".indexes"))
}

var tableName string
Expand All @@ -484,6 +483,12 @@ func (sh *Shell) runCommand(ctx context.Context, in string) error {
return runSaveCmd(ctx, sh.db, engine, path)
case ".schema":
return dbutil.DumpSchema(ctx, sh.db, os.Stdout, cmd[1:]...)
case ".import":
if len(cmd) != 4 {
return stringutil.Errorf(getUsage(".import"))
}

return runImportCmd(ctx, sh.db, cmd[1], cmd[2], cmd[3])
default:
return displaySuggestions(in)
}
Expand All @@ -498,26 +503,6 @@ func (sh *Shell) runQuery(ctx context.Context, q string) error {
return err
}

func (sh *Shell) runPipedInput(ctx context.Context) (ran bool, err error) {
// Check if there is any input being piped in from the terminal
stat, _ := os.Stdin.Stat()
m := stat.Mode()

if (m&os.ModeNamedPipe) == 0 /*cat a.txt| prog*/ && !m.IsRegular() /*prog < a.txt*/ { // No input from terminal
return false, nil
}
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return true, stringutil.Errorf("Unable to read piped input: %w", err)
}
err = sh.runQuery(ctx, string(data))
if err != nil {
return true, stringutil.Errorf("Unable to execute provided sql statements: %w", err)
}

return true, nil
}

func (sh *Shell) changelivePrefix() (string, bool) {
return sh.livePrefix, sh.multiLine
}
Expand Down
4 changes: 4 additions & 0 deletions document/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ func (s sliceArray) GetByIndex(i int) (Value, error) {
func NewFromCSV(headers, columns []string) Document {
fb := NewFieldBuffer()
for i, h := range headers {
if i >= len(columns) {
break
}

fb.Add(h, NewTextValue(columns[i]))
}

Expand Down

0 comments on commit 91580d6

Please sign in to comment.