Improve database usage

This commit is contained in:
Jan-Lukas Else 2021-05-29 13:32:00 +02:00
parent 0219a6302b
commit 29dba59574
15 changed files with 152 additions and 167 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

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

View File

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

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

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