mirror of https://github.com/jlelse/GoBlog
Improve database usage
This commit is contained in:
parent
0219a6302b
commit
29dba59574
|
@ -278,7 +278,7 @@ func apGetRemoteActor(iri string) (*asPerson, int, error) {
|
|||
}
|
||||
|
||||
func apGetAllInboxes(blog string) ([]string, error) {
|
||||
rows, err := appDbQuery("select distinct inbox from activitypub_followers where blog = @blog", sql.Named("blog", blog))
|
||||
rows, err := appDb.query("select distinct inbox from activitypub_followers where blog = @blog", sql.Named("blog", blog))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -295,17 +295,17 @@ func apGetAllInboxes(blog string) ([]string, error) {
|
|||
}
|
||||
|
||||
func apAddFollower(blog, follower, inbox string) error {
|
||||
_, err := appDbExec("insert or replace into activitypub_followers (blog, follower, inbox) values (@blog, @follower, @inbox)", sql.Named("blog", blog), sql.Named("follower", follower), sql.Named("inbox", inbox))
|
||||
_, err := appDb.exec("insert or replace into activitypub_followers (blog, follower, inbox) values (@blog, @follower, @inbox)", sql.Named("blog", blog), sql.Named("follower", follower), sql.Named("inbox", inbox))
|
||||
return err
|
||||
}
|
||||
|
||||
func apRemoveFollower(blog, follower string) error {
|
||||
_, err := appDbExec("delete from activitypub_followers where blog = @blog and follower = @follower", sql.Named("blog", blog), sql.Named("follower", follower))
|
||||
_, err := appDb.exec("delete from activitypub_followers where blog = @blog and follower = @follower", sql.Named("blog", blog), sql.Named("follower", follower))
|
||||
return err
|
||||
}
|
||||
|
||||
func apRemoveInbox(inbox string) error {
|
||||
_, err := appDbExec("delete from activitypub_followers where inbox = @inbox", sql.Named("inbox", inbox))
|
||||
_, err := appDb.exec("delete from activitypub_followers where inbox = @inbox", sql.Named("inbox", inbox))
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ func getBlogStats(blog string) (data map[string]interface{}, err error) {
|
|||
Name, Posts, Chars, Words, WordsPerPost string
|
||||
}
|
||||
// Count total posts
|
||||
row, err := appDbQueryRow("select *, "+wordsPerPost+" from (select "+postCount+", "+charCount+", "+wordCount+" from ("+query+"))", params...)
|
||||
row, err := appDb.queryRow("select *, "+wordsPerPost+" from (select "+postCount+", "+charCount+", "+wordCount+" from ("+query+"))", params...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ func getBlogStats(blog string) (data map[string]interface{}, err error) {
|
|||
return nil, err
|
||||
}
|
||||
// Count posts per year
|
||||
rows, err := appDbQuery("select *, "+wordsPerPost+" from (select year, "+postCount+", "+charCount+", "+wordCount+" from ("+query+") where published != '' group by year order by year desc)", params...)
|
||||
rows, err := appDb.query("select *, "+wordsPerPost+" from (select year, "+postCount+", "+charCount+", "+wordCount+" from ("+query+") where published != '' group by year order by year desc)", params...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ func getBlogStats(blog string) (data map[string]interface{}, err error) {
|
|||
}
|
||||
}
|
||||
// Count posts without date
|
||||
row, err = appDbQueryRow("select *, "+wordsPerPost+" from (select "+postCount+", "+charCount+", "+wordCount+" from ("+query+") where published = '')", params...)
|
||||
row, err = appDb.queryRow("select *, "+wordsPerPost+" from (select "+postCount+", "+charCount+", "+wordCount+" from ("+query+") where published = '')", params...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ func getBlogStats(blog string) (data map[string]interface{}, err error) {
|
|||
months := map[string][]statsTableType{}
|
||||
month := statsTableType{}
|
||||
for _, year := range years {
|
||||
rows, err = appDbQuery("select *, "+wordsPerPost+" from (select month, "+postCount+", "+charCount+", "+wordCount+" from ("+query+") where published != '' and year = @year group by month order by month desc)", append(params, sql.Named("year", year.Name))...)
|
||||
rows, err = appDb.query("select *, "+wordsPerPost+" from (select month, "+postCount+", "+charCount+", "+wordCount+" from ("+query+") where published != '' and year = @year group by month order by month desc)", append(params, sql.Named("year", year.Name))...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
10
comments.go
10
comments.go
|
@ -26,7 +26,7 @@ func serveComment(w http.ResponseWriter, r *http.Request) {
|
|||
serveError(w, r, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
row, err := appDbQueryRow("select id, target, name, website, comment from comments where id = @id", sql.Named("id", id))
|
||||
row, err := appDb.queryRow("select id, target, name, website, comment from comments where id = @id", sql.Named("id", id))
|
||||
if err != nil {
|
||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -66,7 +66,7 @@ func createComment(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
website := strings.TrimSpace(strict.Sanitize(r.FormValue("website")))
|
||||
// Insert
|
||||
result, err := appDbExec("insert into comments (target, comment, name, website) values (@target, @comment, @name, @website)", sql.Named("target", target), sql.Named("comment", comment), sql.Named("name", name), sql.Named("website", website))
|
||||
result, err := appDb.exec("insert into comments (target, comment, name, website) values (@target, @comment, @name, @website)", sql.Named("target", target), sql.Named("comment", comment), sql.Named("name", name), sql.Named("website", website))
|
||||
if err != nil {
|
||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -117,7 +117,7 @@ func buildCommentsQuery(config *commentsRequestConfig) (query string, args []int
|
|||
func getComments(config *commentsRequestConfig) ([]*comment, error) {
|
||||
comments := []*comment{}
|
||||
query, args := buildCommentsQuery(config)
|
||||
rows, err := appDbQuery(query, args...)
|
||||
rows, err := appDb.query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ func getComments(config *commentsRequestConfig) ([]*comment, error) {
|
|||
func countComments(config *commentsRequestConfig) (count int, err error) {
|
||||
query, params := buildCommentsQuery(config)
|
||||
query = "select count(*) from (" + query + ")"
|
||||
row, err := appDbQueryRow(query, params...)
|
||||
row, err := appDb.queryRow(query, params...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -144,6 +144,6 @@ func countComments(config *commentsRequestConfig) (count int, err error) {
|
|||
}
|
||||
|
||||
func deleteComment(id int) error {
|
||||
_, err := appDbExec("delete from comments where id = @id", sql.Named("id", id))
|
||||
_, err := appDb.exec("delete from comments where id = @id", sql.Named("id", id))
|
||||
return err
|
||||
}
|
||||
|
|
111
database.go
111
database.go
|
@ -2,21 +2,23 @@ package main
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
sqlite "github.com/mattn/go-sqlite3"
|
||||
"github.com/schollz/sqlite3dump"
|
||||
)
|
||||
|
||||
var (
|
||||
appDb *sql.DB
|
||||
appDbWriteMutex = &sync.Mutex{}
|
||||
dbStatementCache = map[string]*sql.Stmt{}
|
||||
)
|
||||
var appDb *goblogDb
|
||||
|
||||
type goblogDb struct {
|
||||
db *sql.DB
|
||||
statementCache map[string]*sql.Stmt
|
||||
}
|
||||
|
||||
func initDatabase() (err error) {
|
||||
// Setup db
|
||||
sql.Register("goblog_db", &sqlite.SQLiteDriver{
|
||||
ConnectHook: func(c *sqlite.SQLiteConn) error {
|
||||
if err := c.RegisterFunc("tolocal", toLocalSafe, true); err != nil {
|
||||
|
@ -31,71 +33,87 @@ func initDatabase() (err error) {
|
|||
return nil
|
||||
},
|
||||
})
|
||||
appDb, err = sql.Open("goblog_db", appConfig.Db.File+"?cache=shared&mode=rwc&_journal_mode=WAL")
|
||||
db, err := sql.Open("goblog_db", appConfig.Db.File+"?cache=shared&mode=rwc&_journal_mode=WAL")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = appDb.Ping()
|
||||
db.SetMaxOpenConns(1)
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check available SQLite features
|
||||
rows, err := db.Query("pragma compile_options")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cos := map[string]bool{}
|
||||
var co string
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&co)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cos[co] = true
|
||||
}
|
||||
if _, ok := cos["ENABLE_FTS5"]; !ok {
|
||||
return errors.New("sqlite not compiled with FTS5")
|
||||
}
|
||||
// Migrate DB
|
||||
err = migrateDb(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Create appDB
|
||||
appDb = &goblogDb{
|
||||
db: db,
|
||||
statementCache: map[string]*sql.Stmt{},
|
||||
}
|
||||
appDb.vacuum()
|
||||
addShutdownFunc(func() {
|
||||
_ = closeDb()
|
||||
_ = appDb.close()
|
||||
log.Println("Closed database")
|
||||
})
|
||||
vacuumDb()
|
||||
err = migrateDb()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if appConfig.Db.DumpFile != "" {
|
||||
hourlyHooks = append(hourlyHooks, dumpDb)
|
||||
dumpDb()
|
||||
hourlyHooks = append(hourlyHooks, func() {
|
||||
appDb.dump()
|
||||
})
|
||||
appDb.dump()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dumpDb() {
|
||||
func (db *goblogDb) dump() {
|
||||
f, err := os.Create(appConfig.Db.DumpFile)
|
||||
if err != nil {
|
||||
log.Println("Error while dump db:", err.Error())
|
||||
return
|
||||
}
|
||||
startWritingToDb()
|
||||
defer finishWritingToDb()
|
||||
if err = sqlite3dump.DumpDB(appDb, f); err != nil {
|
||||
if err = sqlite3dump.DumpDB(db.db, f); err != nil {
|
||||
log.Println("Error while dump db:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func startWritingToDb() {
|
||||
appDbWriteMutex.Lock()
|
||||
func (db *goblogDb) close() error {
|
||||
db.vacuum()
|
||||
return db.db.Close()
|
||||
}
|
||||
|
||||
func finishWritingToDb() {
|
||||
appDbWriteMutex.Unlock()
|
||||
func (db *goblogDb) vacuum() {
|
||||
_, _ = db.exec("VACUUM")
|
||||
}
|
||||
|
||||
func closeDb() error {
|
||||
vacuumDb()
|
||||
return appDb.Close()
|
||||
}
|
||||
|
||||
func vacuumDb() {
|
||||
_, _ = appDbExec("VACUUM")
|
||||
}
|
||||
|
||||
func prepareAppDbStatement(query string) (*sql.Stmt, error) {
|
||||
func (db *goblogDb) prepare(query string) (*sql.Stmt, error) {
|
||||
stmt, err, _ := cacheGroup.Do(query, func() (interface{}, error) {
|
||||
stmt, ok := dbStatementCache[query]
|
||||
stmt, ok := db.statementCache[query]
|
||||
if ok && stmt != nil {
|
||||
return stmt, nil
|
||||
}
|
||||
stmt, err := appDb.Prepare(query)
|
||||
stmt, err := db.db.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbStatementCache[query] = stmt
|
||||
db.statementCache[query] = stmt
|
||||
return stmt, nil
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -104,26 +122,29 @@ func prepareAppDbStatement(query string) (*sql.Stmt, error) {
|
|||
return stmt.(*sql.Stmt), nil
|
||||
}
|
||||
|
||||
func appDbExec(query string, args ...interface{}) (sql.Result, error) {
|
||||
stmt, err := prepareAppDbStatement(query)
|
||||
func (db *goblogDb) exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
stmt, err := db.prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
startWritingToDb()
|
||||
defer finishWritingToDb()
|
||||
return stmt.Exec(args...)
|
||||
}
|
||||
|
||||
func appDbQuery(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
stmt, err := prepareAppDbStatement(query)
|
||||
func (db *goblogDb) execMulti(query string, args ...interface{}) (sql.Result, error) {
|
||||
// Can't prepare the statement
|
||||
return db.db.Exec(query, args...)
|
||||
}
|
||||
|
||||
func (db *goblogDb) query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
stmt, err := db.prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stmt.Query(args...)
|
||||
}
|
||||
|
||||
func appDbQueryRow(query string, args ...interface{}) (*sql.Row, error) {
|
||||
stmt, err := prepareAppDbStatement(query)
|
||||
func (db *goblogDb) queryRow(query string, args ...interface{}) (*sql.Row, error) {
|
||||
stmt, err := db.prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -6,9 +6,7 @@ import (
|
|||
"github.com/lopezator/migrator"
|
||||
)
|
||||
|
||||
func migrateDb() error {
|
||||
startWritingToDb()
|
||||
defer finishWritingToDb()
|
||||
func migrateDb(db *sql.DB) error {
|
||||
m, err := migrator.New(
|
||||
migrator.Migrations(
|
||||
&migrator.Migration{
|
||||
|
@ -171,8 +169,5 @@ func migrateDb() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.Migrate(appDb); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return m.Migrate(db)
|
||||
}
|
||||
|
|
|
@ -218,13 +218,13 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (data *indieAuthData) saveAuthorization() (err error) {
|
||||
_, err = appDbExec("insert into indieauthauth (time, code, client, redirect, scope) values (?, ?, ?, ?, ?)", data.time.Unix(), data.code, data.ClientID, data.RedirectURI, strings.Join(data.Scopes, " "))
|
||||
_, err = appDb.exec("insert into indieauthauth (time, code, client, redirect, scope) values (?, ?, ?, ?, ?)", data.time.Unix(), data.code, data.ClientID, data.RedirectURI, strings.Join(data.Scopes, " "))
|
||||
return
|
||||
}
|
||||
|
||||
func (data *indieAuthData) verifyAuthorization() (valid bool, err error) {
|
||||
// code valid for 600 seconds
|
||||
row, err := appDbQueryRow("select code, client, redirect, scope from indieauthauth where time >= ? and code = ? and client = ? and redirect = ?", time.Now().Unix()-600, data.code, data.ClientID, data.RedirectURI)
|
||||
row, err := appDb.queryRow("select code, client, redirect, scope from indieauthauth where time >= ? and code = ? and client = ? and redirect = ?", time.Now().Unix()-600, data.code, data.ClientID, data.RedirectURI)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -239,13 +239,13 @@ func (data *indieAuthData) verifyAuthorization() (valid bool, err error) {
|
|||
data.Scopes = strings.Split(scope, " ")
|
||||
}
|
||||
valid = true
|
||||
_, err = appDbExec("delete from indieauthauth where code = ? or time < ?", data.code, time.Now().Unix()-600)
|
||||
_, err = appDb.exec("delete from indieauthauth where code = ? or time < ?", data.code, time.Now().Unix()-600)
|
||||
data.code = ""
|
||||
return
|
||||
}
|
||||
|
||||
func (data *indieAuthData) saveToken() (err error) {
|
||||
_, err = appDbExec("insert into indieauthtoken (time, token, client, scope) values (?, ?, ?, ?)", data.time.Unix(), data.token, data.ClientID, strings.Join(data.Scopes, " "))
|
||||
_, err = appDb.exec("insert into indieauthtoken (time, token, client, scope) values (?, ?, ?, ?)", data.time.Unix(), data.token, data.ClientID, strings.Join(data.Scopes, " "))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -254,7 +254,7 @@ func verifyIndieAuthToken(token string) (data *indieAuthData, err error) {
|
|||
data = &indieAuthData{
|
||||
Scopes: []string{},
|
||||
}
|
||||
row, err := appDbQueryRow("select time, token, client, scope from indieauthtoken where token = @token", sql.Named("token", token))
|
||||
row, err := appDb.queryRow("select time, token, client, scope from indieauthtoken where token = @token", sql.Named("token", token))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -275,6 +275,6 @@ func verifyIndieAuthToken(token string) (data *indieAuthData, err error) {
|
|||
|
||||
func revokeIndieAuthToken(token string) {
|
||||
if token != "" {
|
||||
_, _ = appDbExec("delete from indieauthtoken where token=?", token)
|
||||
_, _ = appDb.exec("delete from indieauthtoken where token=?", token)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,14 +40,14 @@ func sendNotification(text string) {
|
|||
}
|
||||
|
||||
func saveNotification(n *notification) error {
|
||||
if _, err := appDbExec("insert into notifications (time, text) values (@time, @text)", sql.Named("time", n.Time), sql.Named("text", n.Text)); err != nil {
|
||||
if _, err := appDb.exec("insert into notifications (time, text) values (@time, @text)", sql.Named("time", n.Time), sql.Named("text", n.Text)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteNotification(id int) error {
|
||||
_, err := appDbExec("delete from notifications where id = @id", sql.Named("id", id))
|
||||
_, err := appDb.exec("delete from notifications where id = @id", sql.Named("id", id))
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ func buildNotificationsQuery(config *notificationsRequestConfig) (query string,
|
|||
func getNotifications(config *notificationsRequestConfig) ([]*notification, error) {
|
||||
notifications := []*notification{}
|
||||
query, args := buildNotificationsQuery(config)
|
||||
rows, err := appDbQuery(query, args...)
|
||||
rows, err := appDb.query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ func getNotifications(config *notificationsRequestConfig) ([]*notification, erro
|
|||
func countNotifications(config *notificationsRequestConfig) (count int, err error) {
|
||||
query, params := buildNotificationsQuery(config)
|
||||
query = "select count(*) from (" + query + ")"
|
||||
row, err := appDbQueryRow(query, params...)
|
||||
row, err := appDb.queryRow(query, params...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
func cachePersistently(key string, data []byte) error {
|
||||
date, _ := toLocal(time.Now().String())
|
||||
_, err := appDbExec("insert or replace into persistent_cache(key, data, date) values(@key, @data, @date)", sql.Named("key", key), sql.Named("data", data), sql.Named("date", date))
|
||||
_, err := appDb.exec("insert or replace into persistent_cache(key, data, date) values(@key, @data, @date)", sql.Named("key", key), sql.Named("data", data), sql.Named("date", date))
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ var persistentCacheGroup singleflight.Group
|
|||
|
||||
func retrievePersistentCache(key string) (data []byte, err error) {
|
||||
d, err, _ := persistentCacheGroup.Do(key, func() (interface{}, error) {
|
||||
if row, err := appDbQueryRow("select data from persistent_cache where key = @key", sql.Named("key", key)); err == sql.ErrNoRows {
|
||||
if row, err := appDb.queryRow("select data from persistent_cache where key = @key", sql.Named("key", key)); err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
|
@ -33,6 +33,6 @@ func retrievePersistentCache(key string) (data []byte, err error) {
|
|||
}
|
||||
|
||||
func clearPersistentCache(pattern string) error {
|
||||
_, err := appDbExec("delete from persistent_cache where key like @pattern", sql.Named("pattern", pattern))
|
||||
_, err := appDb.exec("delete from persistent_cache where key like @pattern", sql.Named("pattern", pattern))
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
func allPostAliases() ([]string, error) {
|
||||
var aliases []string
|
||||
rows, err := appDbQuery("select distinct value from post_parameters where parameter = 'aliases' and value != path")
|
||||
rows, err := appDb.query("select distinct value from post_parameters where parameter = 'aliases' and value != path")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ func allPostAliases() ([]string, error) {
|
|||
}
|
||||
|
||||
func servePostAlias(w http.ResponseWriter, r *http.Request) {
|
||||
row, err := appDbQueryRow("select path from post_parameters where parameter = 'aliases' and value = @alias", sql.Named("alias", r.URL.Path))
|
||||
row, err := appDb.queryRow("select path from post_parameters where parameter = 'aliases' and value = @alias", sql.Named("alias", r.URL.Path))
|
||||
if err != nil {
|
||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
|
103
postsDb.go
103
postsDb.go
|
@ -125,80 +125,49 @@ func (p *post) createOrReplace(o *postCreationOptions) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
startWritingToDb()
|
||||
// Create transaction
|
||||
tx, err := appDb.Begin()
|
||||
if err != nil {
|
||||
finishWritingToDb()
|
||||
return err
|
||||
}
|
||||
if !o.new {
|
||||
// Remove old post
|
||||
path := p.Path
|
||||
if o.oldPath != "" {
|
||||
path = o.oldPath
|
||||
}
|
||||
_, err := tx.Exec("delete from posts where path = @path", sql.Named("path", path))
|
||||
// Check if path is already in use
|
||||
if o.new || (p.Path != o.oldPath) {
|
||||
// Post is new or post path was changed
|
||||
newPathExists := false
|
||||
row, err := appDb.queryRow("select exists(select 1 from posts where path = @path)", sql.Named("path", p.Path))
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
finishWritingToDb()
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Check if new path exists
|
||||
postExists := func(path string) (bool, error) {
|
||||
result := 0
|
||||
row := tx.QueryRow("select exists(select 1 from posts where path = @path)", sql.Named("path", path))
|
||||
if err = row.Scan(&result); err != nil {
|
||||
return false, err
|
||||
err = row.Scan(&newPathExists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if newPathExists {
|
||||
// New path already exists
|
||||
return errors.New("post already exists at given path")
|
||||
}
|
||||
return result == 1, nil
|
||||
}
|
||||
if exists, err := postExists(p.Path); err != nil {
|
||||
_ = tx.Rollback()
|
||||
finishWritingToDb()
|
||||
return err
|
||||
} else if exists {
|
||||
_ = tx.Rollback()
|
||||
finishWritingToDb()
|
||||
return errors.New("post already exists at given path")
|
||||
}
|
||||
// Create new post
|
||||
_, err = tx.Exec(
|
||||
`insert into posts (path, content, published, updated, blog, section, status)
|
||||
values (@path, @content, @published, @updated, @blog, @section, @status)`,
|
||||
sql.Named("path", p.Path), sql.Named("content", p.Content), sql.Named("published", p.Published),
|
||||
sql.Named("updated", p.Updated), sql.Named("blog", p.Blog), sql.Named("section", p.Section),
|
||||
sql.Named("status", p.Status))
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
finishWritingToDb()
|
||||
return err
|
||||
}
|
||||
// Create parameters
|
||||
ppStmt, err := tx.Prepare("insert into post_parameters (path, parameter, value) values (@path, @parameter, @value)")
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
finishWritingToDb()
|
||||
return err
|
||||
// Build SQL
|
||||
var sqlBuilder strings.Builder
|
||||
var sqlArgs []interface{}
|
||||
// Delete old post
|
||||
if !o.new {
|
||||
sqlBuilder.WriteString("delete from posts where path = ?;")
|
||||
sqlArgs = append(sqlArgs, o.oldPath)
|
||||
}
|
||||
// Insert new post
|
||||
sqlBuilder.WriteString("insert into posts (path, content, published, updated, blog, section, status) values (?, ?, ?, ?, ?, ?, ?);")
|
||||
sqlArgs = append(sqlArgs, p.Path, p.Content, p.Published, p.Updated, p.Blog, p.Section, p.Status)
|
||||
// Insert post parameters
|
||||
for param, value := range p.Parameters {
|
||||
for _, value := range value {
|
||||
if value != "" {
|
||||
_, err := ppStmt.Exec(sql.Named("path", p.Path), sql.Named("parameter", param), sql.Named("value", value))
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
finishWritingToDb()
|
||||
return err
|
||||
}
|
||||
sqlBuilder.WriteString("insert into post_parameters (path, parameter, value) values (?, ?, ?);")
|
||||
sqlArgs = append(sqlArgs, p.Path, param, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
if tx.Commit() != nil {
|
||||
finishWritingToDb()
|
||||
// Execute
|
||||
_, err = appDb.execMulti(sqlBuilder.String(), sqlArgs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
finishWritingToDb()
|
||||
// Update FTS index, trigger hooks and reload router
|
||||
rebuildFTSIndex()
|
||||
if p.Status == statusPublished {
|
||||
if o.new || o.oldStatus == statusDraft {
|
||||
|
@ -218,7 +187,7 @@ func deletePost(path string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = appDbExec("delete from posts where path = @path", sql.Named("path", p.Path))
|
||||
_, err = appDb.exec("delete from posts where path = @path", sql.Named("path", p.Path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -228,7 +197,7 @@ func deletePost(path string) error {
|
|||
}
|
||||
|
||||
func rebuildFTSIndex() {
|
||||
_, _ = appDbExec("insert into posts_fts(posts_fts) values ('rebuild')")
|
||||
_, _ = appDb.exec("insert into posts_fts(posts_fts) values ('rebuild')")
|
||||
}
|
||||
|
||||
func getPost(path string) (*post, error) {
|
||||
|
@ -344,7 +313,7 @@ func buildPostsQuery(config *postsRequestConfig) (query string, args []interface
|
|||
|
||||
func getPosts(config *postsRequestConfig) (posts []*post, err error) {
|
||||
query, queryParams := buildPostsQuery(config)
|
||||
rows, err := appDbQuery(query, queryParams...)
|
||||
rows, err := appDb.query(query, queryParams...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -379,7 +348,7 @@ func getPosts(config *postsRequestConfig) (posts []*post, err error) {
|
|||
func countPosts(config *postsRequestConfig) (count int, err error) {
|
||||
query, params := buildPostsQuery(config)
|
||||
query = "select count(distinct path) from (" + query + ")"
|
||||
row, err := appDbQueryRow(query, params...)
|
||||
row, err := appDb.queryRow(query, params...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -389,7 +358,7 @@ func countPosts(config *postsRequestConfig) (count int, err error) {
|
|||
|
||||
func allPostPaths(status postStatus) ([]string, error) {
|
||||
var postPaths []string
|
||||
rows, err := appDbQuery("select path from posts where status = @status", sql.Named("status", status))
|
||||
rows, err := appDb.query("select path from posts where status = @status", sql.Named("status", status))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -405,7 +374,7 @@ func allPostPaths(status postStatus) ([]string, error) {
|
|||
|
||||
func allTaxonomyValues(blog string, taxonomy string) ([]string, error) {
|
||||
var values []string
|
||||
rows, err := appDbQuery("select distinct pp.value from posts p left outer join post_parameters pp on p.path = pp.path where pp.parameter = @tax and length(coalesce(pp.value, '')) > 1 and blog = @blog and status = @status", sql.Named("tax", taxonomy), sql.Named("blog", blog), sql.Named("status", statusPublished))
|
||||
rows, err := appDb.query("select distinct pp.value from posts p left outer join post_parameters pp on p.path = pp.path where pp.parameter = @tax and length(coalesce(pp.value, '')) > 1 and blog = @blog and status = @status", sql.Named("tax", taxonomy), sql.Named("blog", blog), sql.Named("status", statusPublished))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -422,7 +391,7 @@ type publishedDate struct {
|
|||
}
|
||||
|
||||
func allPublishedDates(blog string) (dates []publishedDate, err error) {
|
||||
rows, err := appDbQuery("select distinct substr(published, 1, 4) as year, substr(published, 6, 2) as month, substr(published, 9, 2) as day from posts where blog = @blog and status = @status and year != '' and month != '' and day != ''", sql.Named("blog", blog), sql.Named("status", statusPublished))
|
||||
rows, err := appDb.query("select distinct substr(published, 1, 4) as year, substr(published, 6, 2) as month, substr(published, 9, 2) as day from posts where blog = @blog and status = @status and year != '' and month != '' and day != ''", sql.Named("blog", blog), sql.Named("status", statusPublished))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
8
queue.go
8
queue.go
|
@ -12,7 +12,7 @@ func enqueue(name string, content []byte, schedule time.Time) error {
|
|||
if len(content) == 0 {
|
||||
return errors.New("empty content")
|
||||
}
|
||||
_, err := appDbExec("insert into queue (name, content, schedule) values (@name, @content, @schedule)",
|
||||
_, err := appDb.exec("insert into queue (name, content, schedule) values (@name, @content, @schedule)",
|
||||
sql.Named("name", name), sql.Named("content", content), sql.Named("schedule", schedule.UTC().String()))
|
||||
return err
|
||||
}
|
||||
|
@ -25,17 +25,17 @@ type queueItem struct {
|
|||
}
|
||||
|
||||
func (qi *queueItem) reschedule(dur time.Duration) error {
|
||||
_, err := appDbExec("update queue set schedule = @schedule, content = @content where id = @id", sql.Named("schedule", qi.schedule.Add(dur).UTC().String()), sql.Named("content", qi.content), sql.Named("id", qi.id))
|
||||
_, err := appDb.exec("update queue set schedule = @schedule, content = @content where id = @id", sql.Named("schedule", qi.schedule.Add(dur).UTC().String()), sql.Named("content", qi.content), sql.Named("id", qi.id))
|
||||
return err
|
||||
}
|
||||
|
||||
func (qi *queueItem) dequeue() error {
|
||||
_, err := appDbExec("delete from queue where id = @id", sql.Named("id", qi.id))
|
||||
_, err := appDb.exec("delete from queue where id = @id", sql.Named("id", qi.id))
|
||||
return err
|
||||
}
|
||||
|
||||
func peekQueue(name string) (*queueItem, error) {
|
||||
row, err := appDbQueryRow("select id, name, content, schedule from queue where schedule <= @schedule and name = @name order by schedule asc limit 1", sql.Named("name", name), sql.Named("schedule", time.Now().UTC().String()))
|
||||
row, err := appDb.queryRow("select id, name, content, schedule from queue where schedule <= @schedule and name = @name order by schedule asc limit 1", sql.Named("name", name), sql.Named("schedule", time.Now().UTC().String()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
10
sessions.go
10
sessions.go
|
@ -23,7 +23,7 @@ const (
|
|||
|
||||
func initSessions() {
|
||||
deleteExpiredSessions := func() {
|
||||
if _, err := appDbExec("delete from sessions where expires < @now",
|
||||
if _, err := appDb.exec("delete from sessions where expires < @now",
|
||||
sql.Named("now", time.Now().Local().String())); err != nil {
|
||||
log.Println("Failed to delete expired sessions:", err.Error())
|
||||
}
|
||||
|
@ -101,14 +101,14 @@ func (s *dbSessionStore) Delete(r *http.Request, w http.ResponseWriter, session
|
|||
for k := range session.Values {
|
||||
delete(session.Values, k)
|
||||
}
|
||||
if _, err := appDbExec("delete from sessions where id = @id", sql.Named("id", session.ID)); err != nil {
|
||||
if _, err := appDb.exec("delete from sessions where id = @id", sql.Named("id", session.ID)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *dbSessionStore) load(session *sessions.Session) (err error) {
|
||||
row, err := appDbQueryRow("select data, created, modified, expires from sessions where id = @id", sql.Named("id", session.ID))
|
||||
row, err := appDb.queryRow("select data, created, modified, expires from sessions where id = @id", sql.Named("id", session.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ func (s *dbSessionStore) insert(session *sessions.Session) (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := appDbExec("insert into sessions(data, created, modified, expires) values(@data, @created, @modified, @expires)",
|
||||
res, err := appDb.exec("insert into sessions(data, created, modified, expires) values(@data, @created, @modified, @expires)",
|
||||
sql.Named("data", encoded), sql.Named("created", created.Local().String()), sql.Named("modified", modified.Local().String()), sql.Named("expires", expires.Local().String()))
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -168,7 +168,7 @@ func (s *dbSessionStore) save(session *sessions.Session) (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = appDbExec("update sessions set data = @data, modified = @modified where id = @id",
|
||||
_, err = appDb.exec("update sessions set data = @data, modified = @modified where id = @id",
|
||||
sql.Named("data", encoded), sql.Named("modified", time.Now().Local().String()), sql.Named("id", session.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -16,7 +16,7 @@ func shortenPath(p string) (string, error) {
|
|||
}
|
||||
id := getShortPathID(p)
|
||||
if id == -1 {
|
||||
_, err := appDbExec("insert or ignore into shortpath (path) values (@path)", sql.Named("path", p))
|
||||
_, err := appDb.exec("insert or ignore into shortpath (path) values (@path)", sql.Named("path", p))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func getShortPathID(p string) (id int) {
|
|||
if p == "" {
|
||||
return -1
|
||||
}
|
||||
row, err := appDbQueryRow("select id from shortpath where path = @path", sql.Named("path", p))
|
||||
row, err := appDb.queryRow("select id from shortpath where path = @path", sql.Named("path", p))
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func redirectToLongPath(rw http.ResponseWriter, r *http.Request) {
|
|||
serve404(rw, r)
|
||||
return
|
||||
}
|
||||
row, err := appDbQueryRow("select path from shortpath where id = @id", sql.Named("id", id))
|
||||
row, err := appDb.queryRow("select path from shortpath where id = @id", sql.Named("id", id))
|
||||
if err != nil {
|
||||
serve404(rw, r)
|
||||
return
|
||||
|
|
|
@ -84,7 +84,7 @@ func extractMention(r *http.Request) (*mention, error) {
|
|||
|
||||
func webmentionExists(source, target string) bool {
|
||||
result := 0
|
||||
row, err := appDbQueryRow("select exists(select 1 from webmentions where source = ? and target = ?)", source, target)
|
||||
row, err := appDb.queryRow("select exists(select 1 from webmentions where source = ? and target = ?)", source, target)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
@ -103,12 +103,12 @@ func createWebmention(source, target string) (err error) {
|
|||
}
|
||||
|
||||
func deleteWebmention(id int) error {
|
||||
_, err := appDbExec("delete from webmentions where id = @id", sql.Named("id", id))
|
||||
_, err := appDb.exec("delete from webmentions where id = @id", sql.Named("id", id))
|
||||
return err
|
||||
}
|
||||
|
||||
func approveWebmention(id int) error {
|
||||
_, err := appDbExec("update webmentions set status = ? where id = ?", webmentionStatusApproved, id)
|
||||
_, err := appDb.exec("update webmentions set status = ? where id = ?", webmentionStatusApproved, id)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ func buildWebmentionsQuery(config *webmentionsRequestConfig) (query string, args
|
|||
func getWebmentions(config *webmentionsRequestConfig) ([]*mention, error) {
|
||||
mentions := []*mention{}
|
||||
query, args := buildWebmentionsQuery(config)
|
||||
rows, err := appDbQuery(query, args...)
|
||||
rows, err := appDb.query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ func getWebmentions(config *webmentionsRequestConfig) ([]*mention, error) {
|
|||
func countWebmentions(config *webmentionsRequestConfig) (count int, err error) {
|
||||
query, params := buildWebmentionsQuery(config)
|
||||
query = "select count(*) from (" + query + ")"
|
||||
row, err := appDbQueryRow(query, params...)
|
||||
row, err := appDb.queryRow(query, params...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ func (m *mention) verifyMention() error {
|
|||
err = m.verifyReader(resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
if err != nil {
|
||||
_, err := appDbExec("delete from webmentions where source = @source and target = @target", sql.Named("source", m.Source), sql.Named("target", m.Target))
|
||||
_, err := appDb.exec("delete from webmentions where source = @source and target = @target", sql.Named("source", m.Source), sql.Named("target", m.Target))
|
||||
return err
|
||||
}
|
||||
if len(m.Content) > 500 {
|
||||
|
@ -93,10 +93,10 @@ func (m *mention) verifyMention() error {
|
|||
}
|
||||
newStatus := webmentionStatusVerified
|
||||
if webmentionExists(m.Source, m.Target) {
|
||||
_, err = appDbExec("update webmentions set status = @status, title = @title, content = @content, author = @author where source = @source and target = @target",
|
||||
_, err = appDb.exec("update webmentions set status = @status, title = @title, content = @content, author = @author where source = @source and target = @target",
|
||||
sql.Named("status", newStatus), sql.Named("title", m.Title), sql.Named("content", m.Content), sql.Named("author", m.Author), sql.Named("source", m.Source), sql.Named("target", m.Target))
|
||||
} else {
|
||||
_, err = appDbExec("insert into webmentions (source, target, created, status, title, content, author) values (@source, @target, @created, @status, @title, @content, @author)",
|
||||
_, err = appDb.exec("insert into webmentions (source, target, created, status, title, content, author) values (@source, @target, @created, @status, @title, @content, @author)",
|
||||
sql.Named("source", m.Source), sql.Named("target", m.Target), sql.Named("created", m.Created), sql.Named("status", newStatus), sql.Named("title", m.Title), sql.Named("content", m.Content), sql.Named("author", m.Author))
|
||||
sendNotification(fmt.Sprintf("New webmention from %s to %s", m.Source, m.Target))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue