From 5e21e41c4dd56c73369a948f2991b8773b934bb0 Mon Sep 17 00:00:00 2001 From: Bao Trinh Date: Wed, 22 Mar 2023 04:40:18 -0500 Subject: [PATCH] add import function originally posted at github.com/larkery/zsh-histdb/issues/137 Function to parse zsh's histfile and add it to the database. Leverages zsh's history file parsing so it handles newlines and arbitrary characters very well. The other tools suggested in the README require other languages and don't handle multiline commands soundly. Inserts are batched and it's pretty fast. A bit of time is wasted calling `fc -l` to read the timestamps, but it takes ~1s to parse ~20k lines for me so it should be fast enough for most use-cases. Notes: * this sets the `session` to `0` * (sqlite starts autoincrement ids at 1 so it shouldn't coincide with any actual histdb sessions) * `dir` is set to the empty string (`''`) instead of `NULL` because sqlite doesn't let you use `NULL` as part of a key * Added `unique(session, command_id, place_id, start_time) on conflict ignore` constraint to the `history` table so history instances are de-duped. * Useful for importing history file backups that likely contain dupes --- sqlite-history.zsh | 55 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/sqlite-history.zsh b/sqlite-history.zsh index 0b3ba90..698808d 100644 --- a/sqlite-history.zsh +++ b/sqlite-history.zsh @@ -88,7 +88,8 @@ create table history (id integer primary key autoincrement, place_id int references places (id), exit_status int, start_time real, - duration real + duration real, + unique(session, command_id, place_id, start_time) on conflict ignore ) strict; PRAGMA user_version = 2; EOF @@ -136,6 +137,57 @@ where id = (select max(id) from history) and EOF } +_histdb_import() { + emulate -L zsh + setopt HIST_LEX_WORDS + + local _HISTFILE=${1:-$HISTFILE} + # push current history list onto a stack and initialize a new one + fc -p -a + # read all the lines from the given histfile + local HISTFILE=$_HISTFILE + local HISTSIZE=999999999 + local SAVEHIST=0 + fc -R ${1:-$HISTFILE} + print "loaded ${#history} lines from history file: $HISTFILE" >&2 + + local -a histories + local -i i=0 + # history is read into $history associative array, but we don't have access to + # timestamps/durations so parse the output of `fc -l` to get those + fc -l -t %s -d -D 0 | while { read -r histcmd timestamp duration _cmd } { + # the command output by `fc -l` is escaped so use $history[$histcmd] + # instead to get the raw, unescaped characters + # omit empty commands (not sure how this happens. `print -S` maybe?) + [[ -n "${history[$histcmd]}" ]] || continue + ((++i)) + # duration is formatted as m:ss so parse it back into integer seconds + # escape the history command + histories+=("(${timestamp}, $((${duration%:*}*60 + ${duration#*:})), '${history[$histcmd]//'/''}')") + } + + _histdb_init + result=$(_histdb_query_batch <