package sqlitelib
|
|
import (
|
"database/sql"
|
"errors"
|
"os"
|
"runtime"
|
"runtime/debug"
|
"strings"
|
"sync"
|
"time"
|
|
"electron-egg/demo/util"
|
|
"github.com/wallace5303/ee-go/ehelper"
|
"github.com/wallace5303/ee-go/elog"
|
|
_ "github.com/glebarez/go-sqlite"
|
)
|
|
var (
|
db *sql.DB
|
)
|
|
var initDBLock = sync.Mutex{}
|
|
func InitDB(forceRebuild bool) (err error) {
|
initDBLock.Lock()
|
defer initDBLock.Unlock()
|
|
elog.Logger.Infof("Init database")
|
initDBConnection()
|
|
if !forceRebuild {
|
// 检查数据库结构版本,如果版本不一致的话说明改过表结构,需要重建
|
dbVer := GetDatabaseVer()
|
if util.DatabaseVer == dbVer {
|
return
|
}
|
|
elog.Logger.Infof("rebuilding database ......")
|
}
|
|
// 不存在库或者版本不一致都会走到这里
|
CloseDatabase()
|
if ehelper.FileIsExist(util.DBPath) {
|
if err = removeDatabaseFile(); err != nil {
|
elog.Logger.Errorf("remove database file [%s] failed: %s", util.DBPath, err)
|
|
err = nil
|
}
|
}
|
|
initDBConnection()
|
initDBTables()
|
|
return
|
}
|
|
func initDBConnection() {
|
if db != nil {
|
CloseDatabase()
|
}
|
|
dsn := util.DBPath + "?_pragma=busy_timeout(7000)" +
|
"&_pragma=journal_mode(WAL)" +
|
"&_pragma=synchronous(1)" +
|
"&_pragma=mmap_size(2684354560)" +
|
"&_pragma=cache_size(-20480)" +
|
"&_pragma=page_size(32768)" +
|
"&_pragma=case_sensitive_like(OFF)"
|
|
var err error
|
db, err = sql.Open("sqlite", dsn)
|
if err != nil {
|
elog.Logger.Errorf("create database failed: %s", err)
|
}
|
elog.Logger.Infof("DB data source name: %s", dsn)
|
|
db.SetMaxIdleConns(20)
|
db.SetMaxOpenConns(20)
|
db.SetConnMaxLifetime(365 * 24 * time.Hour)
|
}
|
|
func initDBTables() {
|
elog.Logger.Infof("init tables ......")
|
|
_, err := db.Exec("DROP TABLE IF EXISTS stat")
|
if err != nil {
|
elog.Logger.Errorf("drop table [stat] failed: %s", err)
|
}
|
_, err = db.Exec("CREATE TABLE stat ( key varchar ( 255 ) NOT NULL DEFAULT '', value text DEFAULT '', PRIMARY KEY ( key ) );")
|
if err != nil {
|
elog.Logger.Errorf("create table [stat] failed: %s", err)
|
}
|
|
_, err = db.Exec("DROP TABLE IF EXISTS storage")
|
if err != nil {
|
elog.Logger.Errorf("drop table [storage] failed: %s", err)
|
}
|
_, err = db.Exec("CREATE TABLE storage ( name varchar ( 255 ), value text, expires_time datetime );")
|
if err != nil {
|
elog.Logger.Errorf("create table [storage] failed: %s", err)
|
}
|
_, err = db.Exec("CREATE UNIQUE INDEX index_name ON storage ( name ASC );")
|
if err != nil {
|
elog.Logger.Errorf("create INDEX [index_name] failed: %s", err)
|
}
|
|
setDatabaseVer()
|
}
|
|
func CloseDatabase() (err error) {
|
if db == nil {
|
return
|
}
|
|
err = db.Close()
|
debug.FreeOSMemory()
|
runtime.GC()
|
return
|
}
|
|
func removeDatabaseFile() (err error) {
|
err = os.RemoveAll(util.DBPath)
|
if err != nil {
|
return
|
}
|
err = os.RemoveAll(util.DBPath + "-shm")
|
if err != nil {
|
return
|
}
|
err = os.RemoveAll(util.DBPath + "-wal")
|
if err != nil {
|
return
|
}
|
return
|
}
|
|
func beginTx() (tx *sql.Tx, err error) {
|
if tx, err = db.Begin(); err != nil {
|
elog.Logger.Errorf("begin tx failed: %s\n", err)
|
if strings.Contains(err.Error(), "database is locked") {
|
os.Exit(1)
|
}
|
}
|
return
|
}
|
|
func commitTx(tx *sql.Tx) (err error) {
|
if tx == nil {
|
elog.Logger.Errorf("tx is nil")
|
return errors.New("tx is nil")
|
}
|
|
if err = tx.Commit(); err != nil {
|
elog.Logger.Errorf("commit tx failed: %s\n", err)
|
}
|
return
|
}
|
|
func execStmtTx(tx *sql.Tx, stmt string, args ...interface{}) (err error) {
|
elog.Logger.Infof("[sql/sqlitelib/execStmtTx] sql: %s args: %+v", stmt, args)
|
|
if _, err = tx.Exec(stmt, args...); err != nil {
|
if strings.Contains(err.Error(), "database disk image is malformed") {
|
tx.Rollback()
|
CloseDatabase()
|
elog.Logger.Errorf("database disk image [%s] is malformed, please restart SiYuan kernel to rebuild it", util.DBPath)
|
}
|
elog.Logger.Errorf("exec database stmt [%s] failed: %s\n %s", stmt)
|
return
|
}
|
return
|
}
|
|
func prepareExecInsertTx(tx *sql.Tx, stmtSQL string, args []interface{}) (err error) {
|
stmt, err := tx.Prepare(stmtSQL)
|
|
if err != nil {
|
return
|
}
|
|
if _, err = stmt.Exec(args...); err != nil {
|
elog.Logger.Errorf("exec database stmt [%s] failed: %s", stmtSQL, err)
|
return
|
}
|
return
|
}
|
|
func queryRow(query string, args ...interface{}) *sql.Row {
|
elog.Logger.Infof("[sql/sqlitelib/queryRow] sql: %s args: %+v", query, args)
|
query = strings.TrimSpace(query)
|
if query == "" {
|
elog.Logger.Errorf("statement is empty")
|
return nil
|
}
|
return db.QueryRow(query, args...)
|
}
|
|
func query(query string, args ...interface{}) (*sql.Rows, error) {
|
elog.Logger.Infof("[sql/sqlitelib/query] sql: %s args: %+v", query, args)
|
query = strings.TrimSpace(query)
|
if query == "" {
|
return nil, errors.New("statement is empty")
|
}
|
return db.Query(query, args...)
|
}
|