[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/triples fb63dfe44a 12/19: Convert to sqlite.
From: |
ELPA Syncer |
Subject: |
[elpa] externals/triples fb63dfe44a 12/19: Convert to sqlite. |
Date: |
Sat, 5 Nov 2022 11:58:16 -0400 (EDT) |
branch: externals/triples
commit fb63dfe44a27ed06cb9fe95ed41bf40d41bd35fd
Author: Andrew Hyatt <ahyatt@gmail.com>
Commit: Andrew Hyatt <ahyatt@gmail.com>
Convert to sqlite.
Change not yet finalized, this doesn't yet work.
---
triples-test.el | 13 +++---
triples.el | 129 +++++++++++++++++++++++++++++++++-----------------------
2 files changed, 83 insertions(+), 59 deletions(-)
diff --git a/triples-test.el b/triples-test.el
index d94c1e54ec..2844dccf96 100644
--- a/triples-test.el
+++ b/triples-test.el
@@ -16,7 +16,8 @@ easily debug into it.")
(progn
(let ((db (triples-connect db-file)))
(setq triples-test-db-file db-file)
- ,@body))
+ ,@body
+ (triples-close db)))
(delete-file db-file))))
(defun triples-test-connect-db ()
@@ -188,14 +189,12 @@ easily debug into it.")
(triples-add-schema db 'marker)
(triples-set-type db "foo" 'marker)
(should (equal '((1))
- (emacsql db [:select (funcall count) :from triples :where (=
subject $s1)
- :and (= predicate 'base/type) :and (= object
'marker)]
- "foo")))
+ (sqlite-select db "COUNT(*) FROM triples WHERE subject = ?
AND predicate = 'base/type' AND object = 'marker'"
+ (triples-standardize-val "foo"))))
(triples-set-type db "foo" 'marker)
(should (equal '((1))
- (emacsql db [:select (funcall count) :from triples :where (=
subject $s1)
- :and (= predicate 'base/type) :and (= object
'marker)]
- "foo")))))
+ (sqlite-select db "COUNT(*) FROM triples WHERE subject = ?
AND predicate = 'base/type' AND object = 'marker'"
+ (triples-standardize-val "foo"))))))
(ert-deftest triples-readme ()
(triples-test-with-temp-db
diff --git a/triples.el b/triples.el
index 1e52471490..bf9403d9ea 100644
--- a/triples.el
+++ b/triples.el
@@ -4,7 +4,7 @@
;; Author: Andrew Hyatt <ahyatt@gmail.com>
;; Homepage: https://github.com/ahyatt/triples
-;; Package-Requires: ((emacsql "3.0.0") cl-lib (seq "2.0"))
+;; Package-Requires: ((seq "2.0") (emacs29))
;; Keywords: triples, kg, data, sqlite
;; Version: 0.0
;; This program is free software; you can redistribute it and/or
@@ -27,29 +27,24 @@
;; provide an API offering two-way links between all information stored.
(require 'cl-macs)
-(require 'emacsql)
(require 'seq)
;;; Code:
(defun triples-connect (file)
"Connect to the database FILE and make sure it is populated."
- (let* ((db (emacsql-sqlite3 file))
- (triple-table-exists
- (emacsql db [:select name
- :from sqlite_master
- :where (= type table) :and (= name 'triples)])))
- (unless triple-table-exists
- (emacsql db [:create-table triples ([(subject text :not-null)
- (predicate text :not-null)
- (object :not-null)
- (properties)])])
- (emacsql db [:create-index subject_idx :on triples [subject]])
- (emacsql db [:create-index subject_predicate_idx :on triples [subject
predicate]])
- (emacsql db [:create-index predicate_object_idx :on triples [predicate
object]])
- (emacsql db [:create-unique-index
subject_predicate_object_properties_idx :on triples [subject predicate object
properties]]))
+ (let* ((db (sqlite-open file)))
+ (sqlite-execute db "CREATE TABLE IF NOT EXISTS triples(subject TEXT NOT
NULL, predicate TEXT NOT NULL, object TEXT, PROPERTIES TEXT NOT NULL)")
+ (sqlite-execute db "CREATE INDEX IF NOT EXISTS subject_idx ON triples
(subject)")
+ (sqlite-execute db "CREATE INDEX IF NOT EXISTS subject_predicate_idx ON
triples (subject, predicate)")
+ (sqlite-execute db "CREATE INDEX IF NOT EXISTS predicate_object_idx ON
triples (predicate, object)")
+ (sqlite-execute db "CREATE INDEX IF NOT EXISTS
subject_predicate_object_properties_idx ON triples (subject, predicate, object,
properties)")
db))
+(defun triples-close (db)
+ "Close sqlite database DB."
+ (sqlite-close db))
+
(defun triples--ensure-property-val (vec)
"Return a VEC has 4 elements.
We add a bogus value as a property because we want to be able
@@ -81,52 +76,62 @@ values."
"Add a colon to SYM."
(intern (format ":%s" sym)))
+(defun triples-standardize-val (val)
+ "If VAL is a string, return it as enclosed in quotes
+This is done to have compatibility with the way emacsql stores
+values."
+ (if (stringp val)
+ (format "\"%s\"" val)
+ val))
+
(defun triples--add (db op)
"Perform OP on DB."
(pcase (car op)
('replace-subject
(mapc
(lambda (sub)
- (emacsql db [:delete :from triples :where (= subject $s1)] sub))
+ (sqlite-execute db "DELETE FROM TRIPLES WHERE subject = ?"
+ (list (triples-standardize-val sub))))
(triples--subjects (cdr op))))
('replace-subject-type
(mapc (lambda (sub-triples)
(mapc (lambda (type)
;; We have to ignore base, which keeps type information
in general.
(unless (eq type 'base)
- (emacsql db [:delete :from triples :where (= subject
$s1)
- :and (like predicate $r2)]
- (car sub-triples) (format "%s/%%" type))))
+ (sqlite-execute db "DELETE FROM TRIPLES WHERE SUBJECT
= ? AND PREDICATE LIKE ?"
+ (list (triples-standardize-val (car
sub-triples))
+ (format "%s/%%" type)))))
(seq-uniq
(mapcar #'car (mapcar #'triples-combined-to-type-and-prop
(mapcar #'cl-second (cdr
sub-triples)))))))
(triples--group-by-subjects (cdr op)))))
- (mapc (lambda (triple)
- (emacsql db [:replace :into triples
- :values $v1] (triples--ensure-property-val
- (apply #'vector triple))))
+ (mapc (lambda (triple)
+ (sqlite-execute db "REPLACE INTO TRIPLES VALUES (?, ?, ?, ?)"
+ (triples--ensure-property-val
+ (apply #'vector (mapcar #'triples-standardize-val
triple)))))
(cdr op)))
(defun triples-properties-for-predicate (db cpred)
"Return the properties in DB for combined predicate CPRED as a plist."
(mapcan (lambda (row)
(list (intern (format ":%s" (nth 1 row))) (nth 2 row)))
- (emacsql db [:select * :from triples :where (= subject $s1)] cpred)))
+ (sqlite-select db "SELECT * FROM TRIPLES WHERE subject = ?"
+ (list (triples-standardize-val cpred)))))
(defun triples-predicates-for-type (db type)
"Return all predicates defined for TYPE in DB."
(mapcar #'car
- (emacsql db [:select object :from triples :where (= subject $s1)
- :and (= predicate 'schema/property)] type)))
+ (sqlite-select db "SELECT object FROM triples WHERE subject = ? AND
predicate = 'schema/property'"
+ (list (triples-standardize-val type)))))
(defun triples-verify-schema-compliant (db triples)
"Error if TRIPLES is not compliant with schema in DB."
(mapc (lambda (triple)
(pcase-let ((`(,type . ,prop) (triples-combined-to-type-and-prop
(nth 1 triple))))
(unless (or (eq type 'base)
- (emacsql db [:select * :from triples :where (= subject
$s1)
- :and (= predicate 'schema/property) :and
(= object $s2)]
- type prop))
+ (sqlite-select db "SELECT * FROM triples WHERE subject
= ? AND predicate = 'schema/property'
+AND object = ?"
+ (list (triples-standardize-val type)
(triples-standardize-val prop))))
(error "Property %s not found in schema" (nth 1 triple)))))
triples)
(mapc (lambda (triple)
@@ -170,6 +175,19 @@ PROPERTIES is a plist of properties, without TYPE
prefixes."
(triples-verify-schema-compliant db (cdr op))
(triples--add db op)))
+(defmacro triples-with-transaction (db &rest body)
+ "Create a transaction using DB, executing BODY.
+The transaction will abort if an error is thrown."
+ (declare (indent 0) (debug t))
+ (let ((db-var (gensym "db")))
+ `(condition-case
+ (let ((,db-var ,db))
+ (progn
+ (sqlite-transaction ,db-var)
+ ,@body
+ (sqlite-commit ,db-var)))
+ (error (sqlite-rollback ,db-var)))))
+
(defun triples-set-types (db subject &rest combined-props)
"Set all data for types in COMBINED-PROPS in DB for SUBJECT.
COMBINED-PROPS is a plist which takes combined properties such as
@@ -183,7 +201,7 @@ given in the COMBINED-PROPS will be removed."
(plist-put (gethash (triples--decolon type) type-to-plist)
(triples--encolon prop) val) type-to-plist)))
combined-props)
- (emacsql-with-transaction db
+ (triples-with-transaction db
(cl-loop for k being the hash-keys of type-to-plist using (hash-values v)
do (apply #'triples-set-type db subject k v)))))
@@ -212,8 +230,9 @@ PROPERTIES is a plist of properties, without TYPE prefixes."
(cons (cons (nth 2 db-triple) (nth 3 db-triple))
(gethash (nth 1 db-triple) preds))
preds))
- (emacsql db [:select * :from triples :where (= subject $s1)
- :and (like predicate $r2)] subject (format "%s/%%"
type)))
+ (sqlite-select db "SELECT * FROM triples WHERE subject = ? AND
predicate LIKE ?"
+ (list (triples-standardize-val subject)
+ (format "%s/%%" type))))
(append
(cl-loop for k being the hash-keys of preds using (hash-values v)
nconc (list (triples--encolon (cdr
(triples-combined-to-type-and-prop k)))
@@ -230,23 +249,27 @@ PROPERTIES is a plist of properties, without TYPE
prefixes."
db (triples-type-and-prop-to-combined
type pred))
:base/virtual-reversed)))
(when reversed-prop
- (let ((result (emacsql db [:select subject :from triples
:where (= object $s1)
- :and (= predicate $s2)] subject
reversed-prop)))
+ (let ((result
+ (sqlite-select db "SELECT subject FROM triples WHERE
object = ? AND predicate = ?"
+ (triples-standardize-val (subject))
+ reversed-prop)))
(when result (cons (triples--encolon pred) (list (mapcar
#'car result)))))))))))
(defun triples-remove-type (db subject type)
"Remove TYPE for SUBJECT in DB, and all associated data."
- (emacsql-with-transaction db
- (emacsql db [:delete :from triples :where (= subject $s1)
- :and (= predicate 'base/type)] subject)
- (emacsql db [:delete :from triples :where (= subject $s1)
- :and (like $r2)] subject (format "%s/%%" type))))
+ (triples-with-transaction
+ db
+ (sqlite-execute db "DELETE FROM TRIPLES WHERE subject = ? AND PREDICATE =
'base/type' AND object = ?"
+ (list (triples-standardize-val subject) type))
+ (sqlite-execute db "DELETE FROM TRIPLES WHERE subject = ? AND PREDICATE
LIKE ?"
+ (list (triples-standardize-val subject)
+ (format "%s/%%" type)))))
(defun triples-get-types (db subject)
"From DB, get all types for SUBJECT."
- (mapcar #'car (emacsql db [:select object :from triples :where (= subject
$s1)
- :and (= predicate 'base/type)]
- subject)))
+ (mapcar #'car
+ (sqlite-select db "SELECT object FROM triples WHERE subject = ? AND
predicate = 'base/type'"
+ (list (triples-standardize-val subject)))))
(defun triples-get-subject (db subject)
"From DB return all properties for SUBJECT as a single plist."
@@ -260,7 +283,7 @@ PROPERTIES is a plist of properties, without TYPE prefixes."
(defun triples-set-subject (db subject &rest type-vals-cons)
"From DB set properties of SUBJECT to TYPE-VALS-CONS data.
TYPE-VALS-CONS is a list of conses, combining a type and a plist of values."
- (emacsql-with-transaction db
+ (triples-with-transaction db
(triples-delete-subject db subject)
(mapc (lambda (cons)
(apply #'triples-set-type db subject cons))
@@ -268,23 +291,25 @@ TYPE-VALS-CONS is a list of conses, combining a type and
a plist of values."
(defun triples-delete-subject (db subject)
"Delete all data in DB associated with SUBJECT."
- (emacsql-with-transaction db
- (emacsql db [:delete :from triples :where (= subject $s1)] subject)))
+ (sqlite-execute db "DELETE FROM triples WHERE SUBJECT = ?"
+ (list (triples-standardize-val subject))))
(defun triples-search (db cpred text)
"Search DB for instances of combined property CPRED with TEXT."
- (emacsql db [:select * :from triples :where (= predicate $i1)
- :and (like object $r2)] (triples--decolon cpred)
- (format "%%%s%%" text)))
+ (sqlite-select db "SELECT * FROM triples WHERE predicate = ? AND object LIKE
?"
+ (list (triples--decolon cpred)
+ (format "%%%s%%" text))))
(defun triples-with-predicate (db cpred)
"Return all triples in DB with CPRED as its combined predicate."
- (emacsql db [:select * :from triples :where (= predicate $i1)]
(triples--decolon cpred)))
+ (sqlite-select db "SELECT * FROM triples WHERE predicate = ?"
+ (list (triples--decolon cpred))))
(defun triples-subjects-with-predicate-object (db cpred obj)
"Return all subjects in DB with CPRED equal to OBJ."
- (emacsql db [:select subject :from triples :where (= predicate $i1) :and (=
object $s2)]
- (triples--decolon cpred) obj))
+ (sqlite-select db "SELECT subject FROM triples WHERE predicate = ? AND
object = ?"
+ (list (triples--decolon cpred)
+ (triples-standardize-val obj))))
(defun triples-subjects-of-type (db type)
"Return a list of all subjects with a particular TYPE in DB."
- [elpa] branch externals/triples created (now d17b3d6e17), ELPA Syncer, 2022/11/05
- [elpa] externals/triples 7d5aca3bb8 02/19: Fix issue where single element lists were not being treated as lists., ELPA Syncer, 2022/11/05
- [elpa] externals/triples 6f8f3376f1 06/19: Add `triples-set-types'., ELPA Syncer, 2022/11/05
- [elpa] externals/triples 5e8abd2989 01/19: Initial commit of triples module., ELPA Syncer, 2022/11/05
- [elpa] externals/triples cc5629fe5c 07/19: Wrap all database access in `triples-set-types' in a transaction., ELPA Syncer, 2022/11/05
- [elpa] externals/triples 257de87fdc 08/19: Minor code cleanup., ELPA Syncer, 2022/11/05
- [elpa] externals/triples 4627d6ed6d 10/19: Fix minor mistakes in ert tests., ELPA Syncer, 2022/11/05
- [elpa] externals/triples fb63dfe44a 12/19: Convert to sqlite.,
ELPA Syncer <=
- [elpa] externals/triples 0252dad7d1 17/19: Fixes from code review from Stefan Monnier., ELPA Syncer, 2022/11/05
- [elpa] externals/triples d17b3d6e17 19/19: Merge branch 'combined'., ELPA Syncer, 2022/11/05
- [elpa] externals/triples 2dae3d49b9 18/19: Various fixes for emacsql code, which wasn't being tested correctly., ELPA Syncer, 2022/11/05
- [elpa] externals/triples 6afcb290ca 15/19: Support both emacs 29 sqlite and emacsql., ELPA Syncer, 2022/11/05
- [elpa] externals/triples aca95ba7f3 03/19: Ensure that we don't duplicate triples., ELPA Syncer, 2022/11/05
- [elpa] externals/triples d82cc1d6b8 14/19: Finish basic sqlite layer, and fix everything so tests work., ELPA Syncer, 2022/11/05
- [elpa] externals/triples 8d7d3c13f4 05/19: Make the combined to and from functions public., ELPA Syncer, 2022/11/05
- [elpa] externals/triples ad6e329540 04/19: Fix for ert tests broken by the last commit., ELPA Syncer, 2022/11/05
- [elpa] externals/triples fdbbd5f61d 09/19: Added package-requires., ELPA Syncer, 2022/11/05
- [elpa] externals/triples cef7ad3a81 11/19: Remove emacs requirement for now., ELPA Syncer, 2022/11/05