sqlite-to-csv/main.go

105 lines
2.0 KiB
Go

package main
import (
"database/sql"
"encoding/json"
"fmt"
"log"
"os"
"slices"
"strings"
_ "modernc.org/sqlite"
)
func main() {
db, err := sql.Open("sqlite", os.Args[1])
if err != nil {
log.Fatal(err)
}
defer db.Close()
tables, err := Tables(db)
if err != nil {
log.Fatal(err)
}
if len(os.Args) > 2 {
tables = slices.DeleteFunc(tables, func(s string) bool { return !slices.Contains(os.Args[2:], s) })
}
for i, table := range tables {
if err := func() error {
rows, err := db.Query(fmt.Sprintf(`SELECT * FROM %q`, table))
if err != nil {
return err
}
defer rows.Close()
columns, err := rows.Columns()
if err != nil {
return err
}
if i > 0 {
fmt.Println("")
}
fmt.Println("---", table)
fmt.Println(strings.Join(func() []string {
quoted := []string{}
for _, col := range columns {
quoted = append(quoted, fmt.Sprintf("%q", col))
}
return quoted
}(), ","))
row := make([]any, len(columns))
for rows.Next() {
for i := range row {
var a any
row[i] = &a
}
if err := rows.Scan(row...); err != nil {
return err
}
jsons := []string{}
for i := range row {
row[i] = *(row[i].(*any))
if b, ok := row[i].([]byte); ok {
row[i] = string(b)
}
b, _ := json.Marshal(row[i])
jsons = append(jsons, string(b))
}
fmt.Println(strings.Join(jsons, ","))
}
return rows.Err()
}(); err != nil {
log.Fatalf("%s: %v", table, err)
}
}
}
func Tables(db *sql.DB) ([]string, error) {
return Select[string](db, `SELECT name FROM sqlite_schema WHERE type = 'table' AND name NOT LIKE 'sqlite_%'`)
}
func Select[T any](db *sql.DB, q string, args ...any) ([]T, error) {
rows, err := db.Query(q, args...)
if err != nil {
return nil, err
}
defer rows.Close()
results := []T{}
for rows.Next() {
var some T
if err := rows.Scan(&some); err != nil {
return nil, err
}
results = append(results, some)
}
return results, rows.Err()
}