package main import ( "database/sql" "fmt" "log" "os" "time" _ "github.com/mattn/go-sqlite3" ) type DB struct { Ready bool path string readConn *sql.DB writeConn *sql.DB } func (db *DB) Open() error { if db.path == "" { return fmt.Errorf("database path not set") } file, err := os.Open(db.path) if err != nil { if os.IsNotExist(err) { log.Printf("Database file does not exist at %s, creating", db.path) file, err := os.Create(db.path) if err != nil { return fmt.Errorf("failed to create database file: %v", err) } log.Printf("Database created at %s", db.path) file.Close() } else { return fmt.Errorf("failed to open database file: %v", err) } } file.Close() writeConn, err := sql.Open("sqlite3", db.path+"?_journal=WAL&_synchronous=NORMAL") if err != nil { Error.Printf("%++v", err) return err } writeConn.SetMaxOpenConns(1) writeConn.SetConnMaxIdleTime(30 * time.Second) writeConn.SetConnMaxLifetime(30 * time.Second) db.writeConn = writeConn readConn, err := sql.Open("sqlite3", db.path+"?mode=ro&_journal=WAL&_synchronous=NORMAL&_mode=ro") if err != nil { Error.Printf("%++v", err) return err } readConn.SetMaxOpenConns(4) readConn.SetConnMaxIdleTime(30 * time.Second) readConn.SetConnMaxLifetime(30 * time.Second) db.readConn = readConn db.Ready = true return nil } func (db *DB) Init(ddl string) error { if !db.Ready { return fmt.Errorf("database not ready") } var rows map[string]struct{} = make(map[string]struct{}) // TODO: Maybe make this better one day... var expected = map[string]struct{}{ "Bill": {}, "Payment": {}, } res, err := db.readConn.Query("SELECT name FROM sqlite_master WHERE type='table';") if err != nil { Error.Printf("%++v", err) return err } defer res.Close() for res.Next() { var name string err := res.Scan(&name) if err != nil { Error.Printf("%++v", err) return err } rows[name] = struct{}{} } var needsInit bool for table := range expected { if _, ok := rows[table]; !ok { log.Printf("Table %s not found, initializing", table) needsInit = true break } } if !needsInit { log.Printf("Database already initialized") return nil } _, err = db.writeConn.Exec(ddl) if err != nil { Error.Printf("%++v", err) log.Printf("%#v", "Rolling back") _, err2 := db.writeConn.Exec("ROLLBACK;") if err2 != nil { Error.Printf("Error rolling back! %++v", err) } return err } log.Printf("Database init OK") return nil } func (db *DB) Close() error { err := db.writeConn.Close() if err != nil { return err } err = db.readConn.Close() if err != nil { return err } return nil }