Files
electron-egg/go/demo/sql/sqlitelib/sql.go
gaoshuaixing d14241fdf5 feat: v4 demo
2024-12-24 14:56:55 +08:00

209 lines
4.6 KiB
Go

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...)
}