diff --git a/dbmigrations/00028.sql b/dbmigrations/00028.sql new file mode 100644 index 0000000..bc27c20 --- /dev/null +++ b/dbmigrations/00028.sql @@ -0,0 +1,16 @@ +-- Add foreign key constraint to post_parameters table +create table post_parameters_new ( + id integer primary key autoincrement, + path text not null, + parameter text not null, + value text not null default '', + foreign key (path) references posts(path) on update cascade on delete cascade +); +drop view posts_fts_view; +create view posts_fts_view as select p.rowid as id, p.path as path, coalesce(pp.value, '') as title, p.content as content from posts p left outer join (select * from post_parameters_new pp where pp.parameter = 'title') pp on p.path = pp.path; +insert into post_parameters_new select * from post_parameters; +drop table post_parameters; +alter table post_parameters_new rename to post_parameters; +create index index_post_parameters on post_parameters (path, parameter, value); +create index index_post_parameters_par_val_pat on post_parameters (parameter, value, path); +insert into posts_fts(posts_fts) values ('rebuild'); \ No newline at end of file diff --git a/docs/storage.md b/docs/storage.md index e993677..25436c4 100644 --- a/docs/storage.md +++ b/docs/storage.md @@ -21,6 +21,7 @@ post_parameters posts posts_fts queue +reactions sessions shortpath webmentions diff --git a/postsDb.go b/postsDb.go index 6f111e2..b6a50d3 100644 --- a/postsDb.go +++ b/postsDb.go @@ -182,20 +182,18 @@ func (db *database) savePost(p *post, o *postCreationOptions) error { sqlBuilder.WriteString("insert into posts (path, content, published, updated, blog, section, status, priority) values (?, ?, ?, ?, ?, ?, ?, ?);") sqlArgs = append(sqlArgs, p.Path, p.Content, toUTCSafe(p.Published), toUTCSafe(p.Updated), p.Blog, p.Section, p.Status, p.Priority) } else { - // Update old post - sqlBuilder.WriteString("update posts set path = ?, content = ?, published = ?, updated = ?, blog = ?, section = ?, status = ?, priority = ? where path = ?;") - sqlArgs = append(sqlArgs, p.Path, p.Content, toUTCSafe(p.Published), toUTCSafe(p.Updated), p.Blog, p.Section, p.Status, p.Priority, o.oldPath) // Delete post parameters sqlBuilder.WriteString("delete from post_parameters where path = ?;") sqlArgs = append(sqlArgs, o.oldPath) + // Update old post + sqlBuilder.WriteString("update posts set path = ?, content = ?, published = ?, updated = ?, blog = ?, section = ?, status = ?, priority = ? where path = ?;") + sqlArgs = append(sqlArgs, p.Path, p.Content, toUTCSafe(p.Published), toUTCSafe(p.Updated), p.Blog, p.Section, p.Status, p.Priority, o.oldPath) } // Insert post parameters for param, value := range p.Parameters { - for _, value := range value { - if value != "" { - sqlBuilder.WriteString("insert into post_parameters (path, parameter, value) values (?, ?, ?);") - sqlArgs = append(sqlArgs, p.Path, param, value) - } + for _, value := range lo.Filter(value, loStringNotEmpty) { + sqlBuilder.WriteString("insert into post_parameters (path, parameter, value) values (?, ?, ?);") + sqlArgs = append(sqlArgs, p.Path, param, value) } } // Commit transaction @@ -228,7 +226,7 @@ func (a *goBlog) deletePost(path string) error { if strings.HasSuffix(string(p.Status), statusDeletedSuffix) { // Post is already marked as deleted, delete it from database if _, err = a.db.exec( - `begin; delete from posts where path = ?; delete from post_parameters where path = ?; insert or ignore into deleted (path) values (?); commit;`, + `begin; delete from posts where path = ?; insert or ignore into deleted (path) values (?); commit;`, dbNoCache, p.Path, p.Path, p.Path, ); err != nil { return err @@ -249,7 +247,7 @@ func (a *goBlog) deletePost(path string) error { p.Parameters["deleted"] = []string{deletedTime} // Mark post as deleted if _, err = a.db.exec( - `begin; update posts set status = ? where path = ?; delete from post_parameters where path = ? and value = 'deleted'; insert into post_parameters (path, parameter, value) values (?, 'deleted', ?); commit;`, + `begin; update posts set status = ? where path = ?; delete from post_parameters where path = ? and parameter = 'deleted'; insert into post_parameters (path, parameter, value) values (?, 'deleted', ?); commit;`, dbNoCache, p.Status, p.Path, p.Path, p.Path, deletedTime, ); err != nil { return err @@ -298,7 +296,7 @@ func (a *goBlog) undeletePost(path string) error { func (db *database) replacePostParam(path, param string, values []string) error { // Filter empty values - values = lo.Filter(values, func(v string, _ int) bool { return v != "" }) + values = lo.Filter(values, loStringNotEmpty) // Lock post creation db.pcm.Lock() defer db.pcm.Unlock() diff --git a/postsDb_test.go b/postsDb_test.go index 5d58b0f..d1b23a0 100644 --- a/postsDb_test.go +++ b/postsDb_test.go @@ -394,3 +394,49 @@ func Test_replaceParams(t *testing.T) { assert.Len(t, union, 3) } } + +func Test_postDeletesParams(t *testing.T) { + app := &goBlog{ + cfg: createDefaultTestConfig(t), + } + _ = app.initConfig() + _ = app.initDatabase(false) + defer app.db.close() + app.initComponents(false) + + err := app.createPost(&post{ + Path: "/test/abc", + Content: "ABC", + Parameters: map[string][]string{ + "test": { + "ABC", "DEF", "GHI", + }, + }, + }) + require.NoError(t, err) + + // Delete the first time (mark as delete) + err = app.deletePost("/test/abc") + require.NoError(t, err) + + row, err := app.db.queryRow("select count(*) from post_parameters where path = ? and parameter = ?", "/test/abc", "test") + require.NoError(t, err) + + var count int + err = row.Scan(&count) + require.NoError(t, err) + + assert.Equal(t, 3, count) + + // Delete the second time (actually delete) + err = app.deletePost("/test/abc") + require.NoError(t, err) + + row, err = app.db.queryRow("select count(*) from post_parameters where path = ? and parameter = ?", "/test/abc", "test") + require.NoError(t, err) + + err = row.Scan(&count) + require.NoError(t, err) + + assert.Equal(t, 0, count) +} diff --git a/utils.go b/utils.go index a4a516e..7339611 100644 --- a/utils.go +++ b/utils.go @@ -396,3 +396,7 @@ func stringToInt(s string) int { i, _ := strconv.Atoi(s) return i } + +func loStringNotEmpty(s string, _ int) bool { + return s != "" +}