From f454cfb386fd335c10a4c7f7943eb1a80306c38a Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Sat, 13 Feb 2016 00:21:46 +0100 Subject: [PATCH] store: Add a lastAmongDup column to the cmd table. --- store/cmd_hist.go | 46 ++++++++++++++++++++++++++++++++++++++++++++-- store/dir_hist.go | 5 ++++- store/sqlutil.go | 26 ++++++++++++++++++++++++++ store/store.go | 8 ++++---- 4 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 store/sqlutil.go diff --git a/store/cmd_hist.go b/store/cmd_hist.go index 7256f9d0..ba334dce 100644 --- a/store/cmd_hist.go +++ b/store/cmd_hist.go @@ -10,7 +10,45 @@ import ( var ErrNoMatchingCmd = errors.New("no matching command line") func init() { - initTable["cmd"] = `create table if not exists cmd (content text)` + initTable["cmd"] = func(db *sql.DB) error { + _, err := db.Exec(`create table if not exists cmd (content text, lastAmongDup bool)`) + if err != nil { + return err + } + return addLastAmongDup(db) + } +} + +func addLastAmongDup(db *sql.DB) error { + // Upgrade from early version where lastAmongDup is missing. + tx, err := db.Begin() + if err != nil { + return err + } + rows, err := db.Query("pragma table_info(cmd)") + if err != nil { + tx.Rollback() + return err + } + hasLastAmongDup, err := hasColumn(rows, "lastAmongDup") + if err != nil { + tx.Rollback() + return err + } + if !hasLastAmongDup { + _, err := db.Exec("alter table cmd add column lastAmongDup bool") + if err != nil { + tx.Rollback() + return err + } + _, err = db.Exec("update cmd set lastAmongDup = (rowid in (select max(rowid) from cmd group by content));") + if err != nil { + tx.Rollback() + return err + } + } + tx.Commit() + return nil } // NextCmdSeq returns the next sequence number of the command history. @@ -23,7 +61,11 @@ func (s *Store) NextCmdSeq() (int, error) { // AddCmd adds a new command to the command history. func (s *Store) AddCmd(cmd string) error { - _, err := s.db.Exec(`insert into cmd (content) values(?)`, cmd) + _, err := s.db.Exec(`update cmd set lastAmongDup = 0 where content = ?`, cmd) + if err != nil { + return err + } + _, err = s.db.Exec(`insert into cmd (content, lastAmongDup) values(?, 1)`, cmd) return err } diff --git a/store/dir_hist.go b/store/dir_hist.go index 77c909bd..cba0f951 100644 --- a/store/dir_hist.go +++ b/store/dir_hist.go @@ -13,7 +13,10 @@ const ( ) func init() { - initTable["dir"] = `create table if not exists dir (path text unique primary key, score real default 0)` + initTable["dir"] = func(db *sql.DB) error { + _, err := db.Exec(`create table if not exists dir (path text unique primary key, score real default 0)`) + return err + } } // AddDir adds a directory to the directory history. diff --git a/store/sqlutil.go b/store/sqlutil.go new file mode 100644 index 00000000..d2bd4393 --- /dev/null +++ b/store/sqlutil.go @@ -0,0 +1,26 @@ +package store + +import "database/sql" + +func hasColumn(rows *sql.Rows, colname string) (bool, error) { + cols, err := rows.Columns() + if err != nil { + return false, err + } + dests := make([]interface{}, len(cols)) + var name string + for i, col := range cols { + if col == "name" { + dests[i] = &name + } else { + dests[i] = new(interface{}) + } + } + for rows.Next() { + rows.Scan(dests...) + if name == colname { + return true, nil + } + } + return false, rows.Err() +} diff --git a/store/store.go b/store/store.go index 85448f21..3a889d68 100644 --- a/store/store.go +++ b/store/store.go @@ -14,7 +14,7 @@ type Store struct { db *sql.DB } -var initTable = map[string]string{} +var initTable = map[string](func(*sql.DB) error){} // DefaultDB returns the default database for storage. func DefaultDB(dataDir string) (*sql.DB, error) { @@ -37,10 +37,10 @@ func NewStore(dataDir string) (*Store, error) { func NewStoreDB(db *sql.DB) (*Store, error) { st := &Store{db} - for t, q := range initTable { - _, err := db.Exec(q) + for name, fn := range initTable { + err := fn(db) if err != nil { - return nil, fmt.Errorf("failed to initialize table %s: %v", t, q) + return nil, fmt.Errorf("failed to initialize table %s: %v", name, err) } }