2017-11-19 16:00:37 +00:00
|
|
|
package main
|
|
|
|
|
2017-11-23 01:51:23 +00:00
|
|
|
import (
|
|
|
|
"strings"
|
2018-05-29 16:24:47 +00:00
|
|
|
|
|
|
|
sqlite3 "github.com/mattn/go-sqlite3"
|
2017-11-23 01:51:23 +00:00
|
|
|
)
|
|
|
|
|
2017-11-20 15:05:44 +00:00
|
|
|
// Bookmark type
|
2017-11-19 16:00:37 +00:00
|
|
|
type Bookmark struct {
|
2017-11-26 20:17:30 +00:00
|
|
|
URL string `json:"url"`
|
2017-11-23 20:50:06 +00:00
|
|
|
Metadata string `json:"metadata"`
|
|
|
|
Tags []string `json:"tags"`
|
|
|
|
Desc string `json:"desc"`
|
2018-05-27 15:36:03 +00:00
|
|
|
Node *Node `json:"-"`
|
2017-11-19 16:00:37 +00:00
|
|
|
//flags int
|
|
|
|
}
|
|
|
|
|
2018-05-29 16:24:47 +00:00
|
|
|
// Inserts a bookmarks to the passed DB
|
|
|
|
// In case of conflict follow the default rules
|
|
|
|
// which for sqlite is a fail with the error `sqlite3.ErrConstraint`
|
|
|
|
func (bk *Bookmark) InsertInDB(db *DB) {
|
2018-05-27 15:36:03 +00:00
|
|
|
//log.Debugf("Adding bookmark %s", bk.URL)
|
2018-05-27 15:55:27 +00:00
|
|
|
_db := db.Handle
|
2017-11-19 16:00:37 +00:00
|
|
|
|
|
|
|
tx, err := _db.Begin()
|
|
|
|
logPanic(err)
|
|
|
|
|
|
|
|
stmt, err := tx.Prepare(`INSERT INTO bookmarks(URL, metadata, tags, desc, flags) VALUES (?, ?, ?, ?, ?)`)
|
|
|
|
logError(err)
|
|
|
|
defer stmt.Close()
|
|
|
|
|
2017-11-30 15:08:12 +00:00
|
|
|
_, err = stmt.Exec(bk.URL, bk.Metadata, strings.Join(bk.Tags, TagJoinSep), "", 0)
|
2017-11-26 20:17:30 +00:00
|
|
|
sqlErrorMsg(err, bk.URL)
|
2017-11-19 16:00:37 +00:00
|
|
|
|
|
|
|
err = tx.Commit()
|
|
|
|
logError(err)
|
|
|
|
}
|
2018-05-29 16:24:47 +00:00
|
|
|
|
|
|
|
// Inserts or updates a bookmarks to the passed DB
|
|
|
|
// In case of a conflict for a UNIQUE URL constraint,
|
|
|
|
// update the existing bookmark
|
|
|
|
func (bk *Bookmark) InsertOrUpdateInDB(db *DB) {
|
|
|
|
|
|
|
|
var sqlite3Err sqlite3.Error
|
2018-10-26 16:25:07 +00:00
|
|
|
var scannedTags string
|
2018-05-29 16:24:47 +00:00
|
|
|
|
|
|
|
// TODO
|
|
|
|
// When updating we should only ADD tags and not replace previous ones
|
|
|
|
|
|
|
|
//log.Debugf("Adding bookmark %s", bk.URL)
|
|
|
|
_db := db.Handle
|
|
|
|
|
2018-10-26 16:25:07 +00:00
|
|
|
// Prepare statement that does a pure insert only
|
|
|
|
tryInsertBk, err := _db.Prepare(`INSERT INTO
|
|
|
|
bookmarks(URL, metadata, tags, desc,
|
|
|
|
flags) VALUES (?, ?, ?, ?, ?)`)
|
|
|
|
defer tryInsertBk.Close()
|
|
|
|
sqlErrorMsg(err, bk.URL)
|
|
|
|
|
|
|
|
// Prepare statement that updates an existing bookmark in db
|
|
|
|
updateBk, err := _db.Prepare(`UPDATE bookmarks SET metadata=?, tags=?
|
|
|
|
WHERE url=?`)
|
|
|
|
defer updateBk.Close()
|
|
|
|
sqlErrorMsg(err, bk.URL)
|
|
|
|
|
|
|
|
// Stmt to fetch existing bookmark and tags in db
|
|
|
|
getTags, err := _db.Prepare(`SELECT tags FROM bookmarks WHERE url=? LIMIT 1`)
|
|
|
|
defer getTags.Close()
|
|
|
|
sqlErrorMsg(err, bk.URL)
|
|
|
|
|
|
|
|
// Begin transaction
|
2018-05-29 16:24:47 +00:00
|
|
|
tx, err := _db.Begin()
|
|
|
|
logPanic(err)
|
|
|
|
|
|
|
|
// First try to insert the bookmark (assume it's new)
|
2018-10-26 16:25:07 +00:00
|
|
|
_, err = tx.Stmt(tryInsertBk).Exec(
|
|
|
|
bk.URL,
|
|
|
|
bk.Metadata,
|
|
|
|
strings.Join(bk.Tags, TagJoinSep),
|
|
|
|
"", 0,
|
|
|
|
)
|
2018-05-29 16:24:47 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
sqlite3Err = err.(sqlite3.Error)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil && sqlite3Err.Code != sqlite3.ErrConstraint {
|
|
|
|
sqlErrorMsg(err, bk.URL)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We will handle ErrConstraint ourselves
|
|
|
|
|
2018-10-26 16:25:07 +00:00
|
|
|
// ErrConstraint means the bookmark (url) already exists in table,
|
|
|
|
// we need to update it instead.
|
|
|
|
if err != nil && sqlite3Err.Code == sqlite3.ErrConstraint {
|
|
|
|
//log.Debugf("Updating bookmark %s", bk.URL)
|
|
|
|
|
|
|
|
// First get existing tags for this bookmark if any ?
|
|
|
|
res := tx.Stmt(getTags).QueryRow(
|
|
|
|
bk.URL,
|
|
|
|
)
|
|
|
|
res.Scan(&scannedTags)
|
|
|
|
cacheTags := strings.Split(scannedTags, TagJoinSep)
|
|
|
|
|
|
|
|
// If tags are different, merge current bookmark tags and existing tags
|
|
|
|
// Put them in a map first to remove duplicates
|
|
|
|
var tagMap = make(map[string]bool)
|
|
|
|
for _, v := range cacheTags {
|
|
|
|
tagMap[v] = true
|
|
|
|
}
|
|
|
|
for _, v := range bk.Tags {
|
|
|
|
tagMap[v] = true
|
|
|
|
}
|
|
|
|
|
|
|
|
var newTags []string // merged tags
|
|
|
|
|
|
|
|
// Merge in a single slice
|
|
|
|
for k, _ := range tagMap {
|
|
|
|
newTags = append(newTags, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = tx.Stmt(updateBk).Exec(
|
|
|
|
bk.Metadata,
|
|
|
|
strings.Join(newTags, TagJoinSep), // Join tags with a `|`
|
|
|
|
bk.URL,
|
|
|
|
)
|
2018-05-29 16:24:47 +00:00
|
|
|
|
|
|
|
sqlErrorMsg(err, bk.URL)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = tx.Commit()
|
|
|
|
logError(err)
|
|
|
|
|
|
|
|
}
|