Skip to content

Commit adc8230

Browse files
committed
Added ability to open existing SQLite DBs
1 parent abe6720 commit adc8230

File tree

6 files changed

+97
-23
lines changed

6 files changed

+97
-23
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
# csv-sql
2-
Command-line tool to load csv and xlsx files and run sql commands
2+
Command-line tool to load csv and excel (xlsx) files and run sql commands
33

44
[![Go](https://github.com/dhamith93/csv-sql/actions/workflows/go.yml/badge.svg)](https://github.com/dhamith93/csv-sql/actions/workflows/go.yml)
55

66
## Usage
77

88
csv-sql supports loading and saving results as CSV and XLSX files with data processing with SQLite compatible sql commands.
99

10+
Also, this can be used to open existing SQLite DBs and extract data as CSV.
11+
1012
### Loading a file
1113
```
1214
LOAD /path/to/file table_name
1315
```
1416
You can set up headers if the first row is not a header.
1517
For XLSX files, when loading, this will ask to select the sheet of the file to load.
1618

19+
### Opening a existing SQLite DB
20+
```
21+
DB /path/to/db
22+
```
23+
1724
### Creating a new table with a select query
1825
```sql
1926
CREATE TABLE emp_user AS SELECT emp.emp_id, emp.name, user.user_name, user.role FROM emp INNER JOIN user ON emp.user_id = user.id

helpers/completer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ func Completer(d prompt.Document) []prompt.Suggest {
66
s := []prompt.Suggest{
77
{Text: "LOAD", Description: "LOAD /path/to/file table_name"},
88
{Text: "SAVE", Description: "SAVE table_name /path/to/file"},
9+
{Text: "DB", Description: "DB /path/to/sqlite/db"},
910
{Text: "SHOW TABLES", Description: ""},
1011
{Text: "EXIT", Description: ""},
1112
{Text: "SELECT", Description: ""},

helpers/data.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,43 @@ package helpers
33
import (
44
"csv-sql/entity"
55
"database/sql"
6+
"errors"
67
"fmt"
8+
"log"
9+
"os"
710
"strings"
811

912
_ "github.com/mattn/go-sqlite3"
1013
)
1114

15+
func OpenDB(db *sql.DB, dbName string) (*sql.DB, error) {
16+
mimetype, err := GetMimeType(dbName)
17+
if err != nil {
18+
return db, err
19+
}
20+
21+
if mimetype != "application/x-sqlite3" {
22+
return db, errors.New("file is not a sqlite db")
23+
}
24+
25+
db.Close()
26+
db, err = sql.Open("sqlite3", dbName)
27+
if err != nil {
28+
return db, err
29+
}
30+
return db, nil
31+
}
32+
33+
func CreateDB(dbName string) *sql.DB {
34+
file, err := os.Create(dbName)
35+
if err != nil {
36+
log.Fatal(err.Error())
37+
}
38+
file.Close()
39+
db, _ := sql.Open("sqlite3", dbName)
40+
return db
41+
}
42+
1243
func PopulateTables(db *sql.DB, file *entity.File) {
1344
header := ""
1445
for i := 0; i < len(file.Headers); i++ {

helpers/display.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package helpers
22

33
import (
44
"csv-sql/entity"
5-
"fmt"
5+
"database/sql"
66
"os"
77
"strings"
88

@@ -19,9 +19,26 @@ func PrintTable(resultTable entity.Table) {
1919
table.Render()
2020
}
2121

22-
func PrintFiles(files []entity.File) {
23-
fmt.Println("---")
24-
for _, file := range files {
25-
fmt.Printf("Table: %v\nHeaders: %v\nPath: %v\n---\n", file.Table, strings.Join(file.Headers, ", "), file.Path)
22+
func ShowTables(db *sql.DB) {
23+
tableNamesQuery := "SELECT name FROM sqlite_master"
24+
tableNames := GetData(db, tableNamesQuery)
25+
result := make([][]string, 0)
26+
resultTable := entity.Table{
27+
Headers: []string{"table", "columns"},
2628
}
29+
30+
for _, table := range tableNames.Data {
31+
columnNamesQuery := "SELECT name FROM pragma_table_info('" + table[0] + "')"
32+
columnNames := GetData(db, columnNamesQuery)
33+
var columns []string
34+
35+
for _, v1 := range columnNames.Data {
36+
s := strings.Join(v1, ", ")
37+
columns = append(columns, s)
38+
}
39+
result = append(result, []string{table[0], strings.Join(columns, ", ")})
40+
}
41+
42+
resultTable.Data = result
43+
PrintTable(resultTable)
2744
}

helpers/file.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,8 @@ func WriteToCSV(path string, result entity.Table) {
7171
csvWriter.Flush()
7272
csvFile.Close()
7373
}
74+
75+
func IsFile(path string) bool {
76+
_, err := os.Open(path)
77+
return err == nil
78+
}

main.go

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"csv-sql/helpers"
66
"database/sql"
77
"fmt"
8-
"log"
98
"os"
109
"os/exec"
1110
"strconv"
@@ -21,13 +20,18 @@ func main() {
2120
files := make([]entity.File, 0)
2221
tableCount := 0
2322
dbName := "/tmp/csvql_db_" + helpers.RandSeq(10) + ".db"
24-
db := createDB(dbName)
23+
db := helpers.CreateDB(dbName)
2524
defer db.Close()
2625

2726
for {
2827
response := strings.TrimSpace(prompt.Input("cmd > ", helpers.Completer))
2928
responseArr := strings.Fields(response)
3029
if len(responseArr) > 0 {
30+
if response == "SHOW TABLES" {
31+
helpers.ShowTables(db)
32+
continue
33+
}
34+
3135
cmd := strings.ToUpper(responseArr[0])
3236

3337
if cmd == "LOAD" {
@@ -39,13 +43,13 @@ func main() {
3943
break
4044
}
4145

42-
if cmd == "SELECT" {
43-
helpers.PrintTable(helpers.GetData(db, response))
46+
if cmd == "DB" {
47+
db = openDB(responseArr, db, dbName)
4448
continue
4549
}
4650

47-
if cmd == "SHOW" && response == "SHOW TABLES" {
48-
helpers.PrintFiles(files)
51+
if cmd == "SELECT" {
52+
helpers.PrintTable(helpers.GetData(db, response))
4953
continue
5054
}
5155

@@ -66,6 +70,25 @@ func main() {
6670
os.Remove(dbName)
6771
}
6872

73+
func openDB(responseArr []string, db *sql.DB, dbName string) *sql.DB {
74+
if len(responseArr) > 1 {
75+
if helpers.IsFile(responseArr[1]) {
76+
var err error
77+
db, err = helpers.OpenDB(db, responseArr[1])
78+
79+
if err != nil {
80+
fmt.Printf("Error opening DB : %v\n", err.Error())
81+
fmt.Println("Falling back to default DB")
82+
db, _ = helpers.OpenDB(db, dbName)
83+
}
84+
85+
}
86+
} else {
87+
fmt.Println("Not a valid file to open as a DB")
88+
}
89+
return db
90+
}
91+
6992
func loadFile(responseArr []string, files []entity.File, db *sql.DB, tableCount int) []entity.File {
7093
if len(responseArr) == 3 {
7194
path := responseArr[1]
@@ -128,7 +151,7 @@ func loadFile(responseArr []string, files []entity.File, db *sql.DB, tableCount
128151
content = content[1:]
129152
} else {
130153
for {
131-
fmt.Println("Enter " + strconv.Itoa(len(content[0])) + " headers seperated by commas")
154+
fmt.Println("Enter " + strconv.Itoa(len(content[0])) + " headers separated by commas")
132155
headers = strings.Split(strings.TrimSpace(prompt.Input("> ", helpers.Completer)), ",")
133156
if len(content[0]) == len(headers) {
134157
break
@@ -179,16 +202,6 @@ func saveFile(responseArr []string, db *sql.DB, files []entity.File) {
179202
}
180203
}
181204

182-
func createDB(dbName string) *sql.DB {
183-
file, err := os.Create(dbName)
184-
if err != nil {
185-
log.Fatal(err.Error())
186-
}
187-
file.Close()
188-
db, _ := sql.Open("sqlite3", dbName)
189-
return db
190-
}
191-
192205
func validFileType(mimeType string) bool {
193206
return mimeType == "text/csv" || mimeType == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
194207
}

0 commit comments

Comments
 (0)