branch: externals/triples commit b961d0bf1b32202397ff225353a5866a615c1085 Author: Andrew Hyatt <ahy...@gmail.com> Commit: Andrew Hyatt <ahy...@gmail.com>
Create `triples-backup' and some surrounding functionality. Currently this isn't a complete backup solution, more work will need to be done to make it possible for clients to flexibly use this for their purposes. --- CHANGELOG.org | 1 + triples-test.el | 9 +++++++++ triples.el | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/CHANGELOG.org b/CHANGELOG.org index 9b90dbc936..1ad1c559c5 100644 --- a/CHANGELOG.org +++ b/CHANGELOG.org @@ -2,6 +2,7 @@ TITLE: Changelog for the triples module for GNU Emacs. * 0.2 - Create a default database to encourage a shared triple database. Add information on why this is an interesting idea in the README. +- Add support for backups of databases via =triples-backup=. * 0.1.2 - Bugfix release to remove backward compatibility with pre-Emacs 29 versions. * 0.1.1 diff --git a/triples-test.el b/triples-test.el index 44d92ab2da..7bc9587414 100644 --- a/triples-test.el +++ b/triples-test.el @@ -345,6 +345,15 @@ easily debug into it.") (sqlite-select db "SELECT COUNT(*) FROM triples WHERE subject = ? AND predicate = 'base/type' AND object = 'marker'" (list (triples-standardize-val "foo"))))))) +(ert-deftest triples-backup-strategy-daily () + (cl-letf (((symbol-function 'current-time) + (lambda () + (encode-time (iso8601-parse "2023-01-15T12:00Z"))))) + (should (triples-backup-strategy-daily (encode-time (iso8601-parse "2023-01-14T12:00Z")))) + (should (triples-backup-strategy-daily (encode-time (iso8601-parse "2022-01-01T12:00Z")))) + (should-not (triples-backup-strategy-daily (encode-time (iso8601-parse "2023-01-15T12:00Z")))) + (should-not (triples-backup-strategy-daily (encode-time (iso8601-parse "2023-02-01T12:00Z")))))) + (ert-deftest triples-readme () (triples-test-with-temp-db (triples-add-schema db 'person diff --git a/triples.el b/triples.el index 2e83223295..a91a579dd3 100644 --- a/triples.el +++ b/triples.el @@ -46,10 +46,24 @@ available. Builtin is available when the version is Emacs 29 or greater, and emacsql is usable when the `emacsql' package is installed.") +(defconst triples-sqlite-executable "sqlite3" + "If using emacs 29 builtin sqlite, this specifices the executable. +It is invoked to make backups.") + (defconst triples-default-database-filename (locate-user-emacs-file "triples.db") "The default filename triples database. If no database is specified, this file is used.") +(defconst triples-backup-files-to-keep 3 + "How many backups of database to keep. +Backups are stored in the default location, or the location +specified in `backup-directory-alist'") + +(defconst triples-backup-strategy 'triples-backup-strategy-daily + "Function that controls when backups are created. +Each function is called when a change happens, with no arguments, +and returns a non-nil value if a backup should be created.") + (defun triples-connect (&optional file) "Connect to the database FILE and make sure it is populated. If FILE is nil, use `triples-default-database-filename'." @@ -91,6 +105,38 @@ If FILE is nil, use `triples-default-database-filename'." ('builtin (sqlite-close db)) ('emacsql (emacsql-close db)))) +(defun triples-backup (db filename) + "Perform a backup of DB, located at path FILENAME. +This uses the same backup location and names as configured in +variables such as `backup-directory-alist'. Due to the fact that +the database is never opened as a buffer, normal backups will not +work, therefore this function must be called instead. + +Th DB argument is currently unused, but may be used in the future +if emacs's native sqlite gains a backup feature. + +This also will clear excess backup files, according to +`triples-backup-files-to-keep'." + (call-process (pcase triples-sqlite-interface + ('builtin triples-sqlite-executable) + ('emacsql emacsql-sqlite-executable)) + nil nil nil (expand-file-name filename) + (format ".backup '%s'" (expand-file-name + (car (find-backup-file-name + (expand-file-name filename)))))) + (let ((backup-files (file-backup-file-names (expand-file-name filename)))) + (cl-loop for backup-file in (cl-subseq + backup-files + (min triples-backup-files-to-keep + (length backup-files))) + do (delete-file backup-file)))) + +(defun triples-backup-strategy-daily (last-update) + "Backup strategy to create a change daily at most. +LAST-UPDATE is the time of the last update." + (>= (/ (time-convert (time-subtract (current-time) last-update) 'integer) 86400) + 1)) + (defun triples--decolon (sym) "Remove colon from SYM." (intern (string-replace ":" "" (format "%s" sym))))