package nomad import ( "database/sql" "fmt" _ "github.com/mattn/go-sqlite3" ) type Executor interface { Exec(stmt string, args ...interface{}) (sql.Result, error) Query(stmt string, args ...interface{}) (*sql.Rows, error) } func buildArgs(n int) string { s := "" for i := 0; i < n; i++ { s += "?," } return s[:len(s)-1] } type DB struct { path string db *sql.DB } func NewDB(path string) *DB { return &DB{path: path} } func (db *DB) Connect() (err error) { if db.db != nil { return nil } db.db, err = sql.Open("sqlite3", db.path) return err } func (db *DB) Close() error { if db.db == nil { return nil } err := db.db.Close() db.db = nil return err } func (db *DB) Create() (err error) { err = db.Connect() if err != nil { return err } _, err = db.db.Exec(createSQL) return err } func (db *DB) Begin() (tx *sql.Tx, err error) { err = db.Connect() if err != nil { return tx, err } return db.db.Begin() } func (db *DB) executor(tx *sql.Tx) Executor { if tx != nil { return tx } return db.db } func (db *DB) Mark(tx *sql.Tx, item Item) (err error) { _, err = db.executor(tx).Exec(markSQL, item.URL.ID(), item.Title, item.PubDate) return err } func (db *DB) Filter(tx *sql.Tx, items []Item) (newItems []Item, err error) { seen := map[string]bool{} args := buildArgs(len(items)) stmt := fmt.Sprintf(filterSQLTpl, args) urls := make([]interface{}, 0, len(items)) for _, item := range items { urls = append(urls, item.URL.ID()) } rows, err := db.executor(tx).Query(stmt, urls...) if err != nil { return nil, err } for rows.Next() { var url string err = rows.Scan(&url) if err != nil { return nil, err } seen[url] = true } for _, item := range items { if seen[item.URL.ID()] { continue } newItems = append(newItems, item) } // Reverse the list of items so that they are in reverse chronological order. nitems := len(items) - 1 for i := range items { j := nitems - i items[i], items[j] = items[j], items[i] } return newItems, nil }