emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/triples 6afcb290ca 15/19: Support both emacs 29 sqlite


From: ELPA Syncer
Subject: [elpa] externals/triples 6afcb290ca 15/19: Support both emacs 29 sqlite and emacsql.
Date: Sat, 5 Nov 2022 11:58:17 -0400 (EDT)

branch: externals/triples
commit 6afcb290ca87934fb230789e3c99c64a4002ecd5
Author: Andrew Hyatt <ahyatt@gmail.com>
Commit: Andrew Hyatt <ahyatt@gmail.com>

    Support both emacs 29 sqlite and emacsql.
---
 README.org      |   4 +-
 triples-test.el | 204 +++++++++++++++++++++++++++++-------------------
 triples.el      | 235 ++++++++++++++++++++++++++++++++++++++++----------------
 3 files changed, 295 insertions(+), 148 deletions(-)

diff --git a/README.org b/README.org
index 214e4f663f..66ab22eb64 100644
--- a/README.org
+++ b/README.org
@@ -1,6 +1,6 @@
 #+TITLE: Triples
 
-The =triples= module is a standard database module designed for use in other 
emacs modules.  It works with the =sqlite= module, and provides a simple way of 
storing entities and their associated schema.  The triples module is well 
suited to graph-like applications, where links between entities are important.  
The module has wrappers for most common operations, but it is anticipated that 
occasionally client modules would need to make their own =sqlite= calls.  Many 
database can be handled [...]
+The =triples= module is a standard database module designed for use in other 
emacs modules.  It works with either the builtin sqlite in Emacs 29 or the 
[[https://github.com/magit/emacsql][emacsql]] module, and provides a simple way 
of storing entities and their associated schema.  The triples module is well 
suited to graph-like applications, where links between entities are important.  
The module has wrappers for most common operations, but it is anticipated that 
occasionally client modu [...]
 
 * Maturity
 This module is very new should be considered alpha quality.
@@ -73,7 +73,7 @@ There are other useful functions, including:
 - =triples-with-predicate=, gets all triples that is about a specific property,
 - =triples-with-predicate-object=, get all subjects whose predicate is equal 
to /object/,
 - =triples-subjects-of-type=, get all subjects which have a particular type.
-* Preciate, with type and without
+* Predicates, with type and without
 Sometimes the triples library will require predicates that are without type, 
and sometimes with type, or "combined predicates".  The rule is that if the 
type is already specified in the function, it does not need to be respecified.  
If the type is not specified, it is included in the combined predicate.
 
 When returning data, if data is from just one type, the type is not returned 
in the returned predicates.  If the data is from multiple types, the type is 
returned as combined predicates.
diff --git a/triples-test.el b/triples-test.el
index 2c9afe098d..784557fc67 100644
--- a/triples-test.el
+++ b/triples-test.el
@@ -1,7 +1,10 @@
 ;;; triples-test.el --- Tests for triples module.  -*- lexical-binding: t; -*-
+;; Note: It's important to test this on emacs 29, with emacsql installed, so we
+;; can make both types of sqlite backend work.
 (require 'triples)
 (require 'seq)
 (require 'kv)
+(require 'emacsql)
 
 ;;; Code:
 
@@ -26,86 +29,127 @@ easily debug into it.")
   (let ((sql-database triples-test-db-file))
     (sql-sqlite (format "*schema test db SQL %s*" triples-test-db-file))))
 
-(ert-deftest triples-test-insert ()
-  (triples-test-with-temp-db
-    (triples--insert db "sub" 'pred "obj")
-    ;; Test for emacsql compability
-    (should (equal (sqlite-select db "SELECT * FROM triples")
-                   '(("\"sub\"" "pred" "\"obj\"" "()"))))
-    ;; Test that it replaces - this shouldn't result in two rows.
-    (triples--insert db "sub" 'pred "obj")
-    (should (equal (sqlite-select db "SELECT count(*) FROM triples")
-                   '((1))))
-    ;; Test that colons in the predicate are stripped away when stored.
-    (triples--insert db "sub" :test/pred "obj")
-    (should (equal (sqlite-select db "SELECT count(*) FROM triples WHERE 
predicate = ?"
-                                  '("test/pred"))
-                   '((1))))
-    ;; Test we correctly test for bad inputs.
-    (should-error (triples--insert db "sub" "pred" "obj"))
-    (should-error (triples--insert db "sub" 'pred "obj" '(ordinary-list)))
-    (should-error (triples--insert db "sub" 'pred "obj" "string"))
-    ;; Test that we can have symbol subject and objects
-    (triples--insert db 'sub 'pred 'obj)
-    (should (equal (sqlite-select db "SELECT * FROM triples WHERE subject = ?" 
'("sub"))
-                   '(("sub" "pred" "obj" "()"))))))
-
-(ert-deftest triples-test-delete ()
-  (triples-test-with-temp-db
-    (triples--insert db 1 'pred 2)
-    (triples--insert db 2 'pred 1)
-    (triples--delete db 1)
-    (should (equal (sqlite-select db "SELECT count(*) FROM triples")
-                   '((1))))
-    (should (equal (sqlite-select db "SELECT count(*) FROM triples WHERE 
subject = ?" '(1))
-                   '((0))))
-    (triples--insert db 1 'pred 2)
-    (triples--delete db nil nil 2)
-    (should (equal (sqlite-select db "SELECT count(*) FROM triples WHERE 
object = ?" '(2))
-                   '((0))))
-    (triples--insert db 1 'pred 2)
-    (triples--delete db nil 'pred nil)
-    (should (equal (sqlite-select db "SELECT count(*) FROM triples")
-                   '((0))))))
-
-(ert-deftest triples-test-delete-subject-predicate-prefix ()
-  (triples-test-with-temp-db
-    (triples--insert db 1 'test/foo 2)
-    (triples--insert db 1 'bar/bar 1)
-    (triples--delete-subject-predicate-prefix db 1 'test)
-    (should (equal (sqlite-select db "SELECT count(*) FROM triples")
-                   '((1))))
-    ;; Make sure colons are stripped.
-    (triples--delete-subject-predicate-prefix db 1 :bar)
-    (should (equal (sqlite-select db "SELECT count(*) FROM triples")
-                   '((0))))))
-
-(ert-deftest triples-test-select ()
-  (triples-test-with-temp-db
-    (triples--insert db 1 'pred 2 '(:a 1))
-    (let ((expected '((1 pred 2 (:a 1)))))
-      (should (equal (triples--select db 1) expected))
-      (should (equal (triples--select db nil 'pred) expected))
-      (should (equal (triples--select db nil nil 2) expected))
-      (should (equal (triples--select db 1 nil 2) expected))
-      (should (equal (triples--select db 1 'pred 2) expected))
-      (should (equal '((1)) (triples--select db 1 nil nil nil '(subject))))
-      (should (equal '((1 pred)) (triples--select db 1 nil nil nil '(subject 
predicate)))))))
-
-(ert-deftest triples-test-select-with-pred-prefix ()
-  (triples-test-with-temp-db
-    (triples--insert db 'sub1 'pred/foo 'obj)
-    (triples--insert db 'sub1 'pred/bar 'obj)
-    (triples--insert db 'sub2 'pred/foo 'obj)
-    (should (equal (triples-test-list-sort (triples--select-pred-prefix db 
'sub1 'pred))
-                   (triples-test-list-sort '((sub1 pred/foo obj nil)
-                                             (sub1 pred/bar obj nil)))))))
-
-(ert-deftest triples-test-select-predicate-object-fragment ()
-  (triples-test-with-temp-db
-    (triples--insert db 'sub1 'pred/foo "a whole phrase")
-    (should (equal (triples--select-predicate-object-fragment db 'pred/foo 
"whole")
-                   '((sub1 pred/foo "a whole phrase" nil))))))
+(defun triples-test-insert (mode)
+  (let ((triples--sqlite-interface mode))
+    (triples-test-with-temp-db
+      (triples--insert db "sub" 'pred "obj")
+      (should (equal (triples--select db)
+                     '(("sub" pred "obj" nil))))
+      ;; Test that we actually are storing with builtin something compatible
+      ;; with emacsql.
+      (when (eq mode 'builtin)
+        (should (equal (sqlite-select db "SELECT * FROM triples")
+                       '(("\"sub\"" "pred" "\"obj\"" "()")))))
+      ;; Test that it replaces - this shouldn't result in two rows.
+      (triples--insert db "sub" 'pred "obj")
+      (should (= (length (triples--select db)) 1))
+      ;; Test that colons in the predicate are stripped away when stored.
+      (triples--insert db "sub" :test/pred "obj")
+      (should (= (length (triples--select db nil 'test/pred)) 1))
+      ;; Test we correctly test for bad inputs.
+      (should-error (triples--insert db "sub" "pred" "obj"))
+      (should-error (triples--insert db "sub" 'pred "obj" '(ordinary-list)))
+      (should-error (triples--insert db "sub" 'pred "obj" "string"))
+      ;; Test that we can have symbol subject and objects
+      (triples--insert db 'sub 'pred 'obj)
+      (should (equal
+               (triples--select db 'sub)
+               '((sub pred obj nil)))))))
+
+(ert-deftest triples-test-insert-builtin ()
+  (triples-test-insert 'builtin))
+
+(ert-deftest triples-test-insert-emacsql ()
+  (triples-test-insert 'emacsql))
+
+(defun triples-test-delete (mode)
+  (let ((triples--sqlite-interface mode))
+    (triples-test-with-temp-db
+     (triples--insert db 1 'pred 2)
+     (triples--insert db 2 'pred 1)
+     (triples--delete db 1)
+     (should (= 1 (length (triples--select db))))
+     (should (= 0 (length (triples--select db 1))))
+     (triples--insert db 1 'pred 2)
+     (triples--delete db nil nil 2)
+     (should (= 0 (length (triples--select db nil nil 2))))
+     (triples--insert db 1 'pred 2)
+     (triples--delete db nil 'pred nil)
+     (should (= 0 (length (triples--select db)))))))
+
+(ert-deftest triples-test-delete-builtin ()
+  (triples-test-delete 'builtin))
+
+(ert-deftest triples-test-delete-emacsql ()
+  (triples-test-delete 'emacsql))
+
+(defun triples-test-delete-subject-predicate-prefix (mode)
+  (let ((triples--sqlite-interface mode))
+    (triples-test-with-temp-db
+     (triples--insert db 1 'test/foo 2)
+     (triples--insert db 1 'bar/bar 1)
+     (triples--delete-subject-predicate-prefix db 1 'test)
+     (should (= 1 (length (triples--select db))))
+     ;; Make sure colons are stripped.
+     (triples--delete-subject-predicate-prefix db 1 :bar)
+     (should (= 0 (length (triples--select db)))))))
+
+(ert-deftest triples-test-delete-subject-predicate-prefix-builtin ()
+  (triples-test-delete-subject-predicate-prefix 'builtin))
+
+(ert-deftest triples-test-delete-subject-predicate-prefix-emacsql ()
+  (triples-test-delete-subject-predicate-prefix 'emacsql))
+
+(defun triples-test-select (mode)
+  (let ((triples--sqlite-interface mode))
+    (triples-test-with-temp-db
+     (triples--insert db 1 'pred 2 '(:a 1))
+     (let ((expected '((1 pred 2 (:a 1)))))
+       (should (equal (triples--select db 1) expected))
+       (should (equal (triples--select db nil 'pred) expected))
+       (should (equal (triples--select db nil nil 2) expected))
+       (should (equal (triples--select db 1 nil 2) expected))
+       (should (equal (triples--select db 1 'pred 2) expected))
+       (should (equal '((1)) (triples--select db 1 nil nil nil '(subject))))
+       (should (equal '((1 pred)) (triples--select db 1 nil nil nil '(subject 
predicate))))))))
+
+(ert-deftest triples-test-select-builtin ()
+  (triples-test-select 'builtin))
+
+(ert-deftest triples-test-select-emacsql ()
+  (triples-test-select 'emacsql))
+
+(defun triples-test-select-with-pred-prefix (mode)
+  (let ((triples--sqlite-interface mode))
+    (triples-test-with-temp-db
+     (triples--insert db 'sub1 'pred/foo 'obj)
+     (triples--insert db 'sub1 'pred/bar 'obj)
+     (triples--insert db 'sub2 'pred/foo 'obj)
+     (should (equal (triples-test-list-sort (triples--select-pred-prefix db 
'sub1 'pred))
+                    (triples-test-list-sort '((sub1 pred/foo obj nil)
+                                              (sub1 pred/bar obj nil))))))))
+
+(ert-deftest triples-test-select-with-pred-prefix-builtin ()
+  (triples-test-select 'builtin))
+
+(ert-deftest triples-test-select-with-pred-prefix-emacsql ()
+  (triples-test-select 'emacsql))
+
+(defun triples-test-select-predicate-object-fragment (mode)
+  (let ((triples--sqlite-interface mode))
+    (triples-test-with-temp-db
+     (triples--insert db 'sub1 'pred/foo "a whole phrase")
+     (should (equal (triples--select-predicate-object-fragment db 'pred/foo 
"whole")
+                    '((sub1 pred/foo "a whole phrase" nil)))))))
+
+(ert-deftest triples-test-select-predicate-object-fragment-builtin ()
+  (triples-test-select-predicate-object-fragment 'builtin))
+
+(ert-deftest triples-test-select-predicate-object-fragment-emacsql ()
+  (triples-test-select-predicate-object-fragment 'emacsql))
+
+;; After this we don't bother testing both with emacsql and the builtin sqlite,
+;; since if the functions tested above work, it should also work for both.
 
 (defun triples-test-op-equals (result target)
   (and (equal (car result) (car target))
diff --git a/triples.el b/triples.el
index 37c51e7e3c..ca42b023db 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: ((seq "2.0") (emacs29))
+;; Package-Requires: ((seq "2.0"))
 ;; Keywords: triples, kg, data, sqlite
 ;; Version: 0.0
 ;; This program is free software; you can redistribute it and/or
@@ -25,6 +25,9 @@
 ;; triples: subject, predicate, objects, plus some extra metadata. This data
 ;; structure provides a way to store data according to an extensible schema, 
and
 ;; provide an API offering two-way links between all information stored.
+;;
+;; This package requires either emacs 29 or the emacsql package to be 
installed.
+
 
 (require 'cl-macs)
 (require 'seq)
@@ -32,33 +35,58 @@
 
 ;;; Code:
 
+(defvar triples-sqlite-interface 'builtin
+  "The interface to sqlite to use.
+Either `builtin' or `emacsql'. This only is used when the version
+is emacs 29 or greater (when builtin is available) and emacsql
+package is installed, otherwise triples will just use what is
+available.")
+
+(defun triples--sqlite-interface ()
+  "Return the sqlite interface to use.
+See the `triples-sqlite-interface' variable for more information.
+This will return either `builtin' or `emacsql'."
+  (if (and (>= emacs-major-version 29)
+           (featurep 'emacsql))
+      triples-sqlite-interface
+    (if (>= emacs-major-version 29) 'builtin 'emacsql)))
+
 (defun triples-connect (file)
   "Connect to the database FILE and make sure it is populated."
-  (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 UNIQUE INDEX IF NOT EXISTS 
subject_predicate_object_properties_idx ON triples (subject, predicate, object, 
properties)")
-    db))
+  (unless (or (>= emacs-major-version 29)
+              (featurep 'emacsql))
+    (error "The triples package requires either emacs 29 or the emacsql 
package to be installed."))
+  (pcase (triples--sqlite-interface)
+    ('builtin (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 UNIQUE INDEX IF NOT EXISTS 
subject_predicate_object_properties_idx ON triples (subject, predicate, object, 
properties)")
+                db))
+    ('emacsql
+     (require 'emacsql)
+     (let* ((db (emacsql-sqlite 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]]))
+       db))))
 
 (defun triples-close (db)
   "Close sqlite database DB."
-  (sqlite-close db))
-
-(defun triples--subjects (triples)
-  "Return all unique subjects in TRIPLES."
-  (seq-uniq (mapcar #'car triples)))
-
-(defun triples--group-by-subjects (triples)
-  "Return an alist of subject to TRIPLES with that subject."
-  (let ((subj-to-triples (make-hash-table :test #'equal)))
-    (dolist (triple triples)
-      (puthash (car triple)
-               (cons triple (gethash (car triple) subj-to-triples))
-               subj-to-triples))
-    (cl-loop for k being the hash-keys of subj-to-triples using (hash-values v)
-             collect (cons k v))))
+  (pcase (triples--sqlite-interface)
+    ('builtin (sqlite-close db))
+    ('emacsql (emacsql-close db))))
 
 (defun triples--decolon (sym)
   "Remove colon from SYM."
@@ -103,76 +131,151 @@ normal schema checks, so should not be called from 
client programs."
     (error "Predicates in triples must always be symbols"))
   (unless (plistp properties)
     (error "Properties stored must always be plists"))
-  (sqlite-execute db "REPLACE INTO TRIPLES VALUES (?, ?, ?, ?)"
-                  (list (triples-standardize-val subject)
-                        (triples-standardize-val (triples--decolon predicate))
-                        (triples-standardize-val object)
-                        ;; Properties cannot be null, since in sqlite each 
null value
-                        ;; is distinct from each other, so replace would not 
replace
-                        ;; duplicate triples each with null properties.
-                        (triples-standardize-val properties))))
+  (pcase (triples--sqlite-interface)
+    ('builtin 
+     (sqlite-execute db "REPLACE INTO triples VALUES (?, ?, ?, ?)"
+                     (list (triples-standardize-val subject)
+                           (triples-standardize-val (triples--decolon 
predicate))
+                           (triples-standardize-val object)
+                           ;; Properties cannot be null, since in sqlite each 
null value
+                           ;; is distinct from each other, so replace would 
not replace
+                           ;; duplicate triples each with null properties.
+                           (triples-standardize-val properties))))
+    ('emacsql
+     (emacsql db [:replace :into triples :values $v1]
+              [subject predicate object (triples-standardize-val 
properties)]))))
+
+(defun triples--emacsql-andify (wc)
+  "In emacsql where clause WC, insert `:and' between query elements.
+Returns the new list with the added `:and.'s. The first element
+MUST be there `:where' clause. This does reverse the clause
+elements, but it shouldn't matter."
+  (cons (car wc) ;; the :where clause
+        (let ((clauses (cdr wc))
+              (result))
+          (while clauses
+            (push (car clauses) result)
+            (if (cdr clauses) (push :and result))
+            (setq clauses (cdr clauses)))
+          result)))
 
 (defun triples--delete (db &optional subject predicate object properties)
   "Delete triples matching SUBJECT, PREDICATE, OBJECT, PROPERTIES.
 If any of these are nil, they will not selected for. If you set
 all to nil, everything will be deleted, so be careful!"
-  (sqlite-execute
-   db
-   (concat "DELETE FROM TRIPLES"
-           (when (or subject predicate object properties)
-             (concat " WHERE "
-                     (string-join
-                      (seq-filter #'identity
-                                  (list (when subject "SUBJECT = ?")
-                                        (when predicate "PREDICATE = ?")
-                                        (when object "OBJECT = ?")
-                                        (when properties "PROPERTIES = ?")))
-                      " AND "))))
-   (mapcar #'triples-standardize-val (seq-filter #'identity (list subject 
predicate object properties)))))
+  (pcase (triples--sqlite-interface)
+    ('builtin (sqlite-execute
+               db
+               (concat "DELETE FROM triples"
+                       (when (or subject predicate object properties)
+                         (concat " WHERE "
+                                 (string-join
+                                  (seq-filter #'identity
+                                              (list (when subject "SUBJECT = 
?")
+                                                    (when predicate "PREDICATE 
= ?")
+                                                    (when object "OBJECT = ?")
+                                                    (when properties 
"PROPERTIES = ?")))
+                                  " AND "))))
+               (mapcar #'triples-standardize-val (seq-filter #'identity (list 
subject predicate object properties)))))
+    ('emacsql (emacsql db
+                       (apply #'vector
+                              (append '(:delete :from triples)
+                                      (when (or subject predicate object 
properties)
+                                        (triples--emacsql-andify 
+                                         (append
+                                          '(:where)
+                                          (when subject '((= subject $s1)))
+                                          (when predicate '((= predicate $r2)))
+                                          (when object '((= object $s3)))
+                                          (when properties '((= properties 
$r4))))))))
+                       (seq-filter #'identity (list subject predicate object 
properties))))))
 
 (defun triples--delete-subject-predicate-prefix (db subject pred-prefix)
   "Delete triples matching SUBJECT and predicates with PRED-PREFIX."
   (unless (symbolp pred-prefix)
     (error "Predicates in triples must always be symbols"))
-  (sqlite-execute db "DELETE FROM TRIPLES WHERE subject = ? AND predicate LIKE 
?"
+  (pcase (triples--sqlite-interface)
+    ('builtin (sqlite-execute db "DELETE FROM triples WHERE subject = ? AND 
predicate LIKE ?"
                   (list (triples-standardize-val subject)
                         (format "%s/%%" (triples--decolon pred-prefix)))))
+    ('emacsql (emacsql db [:delete :from triples :where (= subject $s1) :and 
(like predicate $r2)]
+                       subject (format "%s/%%" (triples--decolon 
pred-prefix))))))
 
 (defun triples--select-pred-prefix (db subject pred-prefix)
   "Return rows matching SUBJECT and PRED-PREFIX."
-  (mapcar (lambda (row) (mapcar #'triples-standardize-result row))
+  (pcase (triples--sqlite-interface)
+    ('builtin (mapcar (lambda (row) (mapcar #'triples-standardize-result row))
           (sqlite-select db "SELECT * FROM triples WHERE subject = ? AND 
predicate LIKE ?"
                          (list (triples-standardize-val subject)
                                (format "%s/%%" pred-prefix)))))
+    ('emacsql (emacsql db [:select * :from triples :where (= subject $s1) :and 
(like predicate $r2)]
+                       subject (format "%s/%%" pred-prefix)))))
 
 (defun triples--select-predicate-object-fragment (db predicate object-fragment)
   "Return rows with PREDICATE and with OBJECT-FRAGMENT in object."
-  (mapcar (lambda (row) (mapcar #'triples-standardize-result row))
-          (sqlite-select db "SELECT * from triples WHERE predicate = ? AND 
object LIKE ?"
-                         (list (triples-standardize-val predicate)
-                               (format "%%%s%%" object-fragment)))))
+  (pcase (triples--sqlite-interface)
+    ('builtin (mapcar (lambda (row) (mapcar #'triples-standardize-result row))
+                      (sqlite-select db "SELECT * from triples WHERE predicate 
= ? AND object LIKE ?"
+                                     (list (triples-standardize-val predicate)
+                                           (format "%%%s%%" 
object-fragment)))))
+    ('emacsql (emacsql db [:select * :from triples :where (= predicate $r1) 
:and (like object $s2)]
+                       predicate (format "%%%s%%" object-fragment)))))
 
 (defun triples--select (db &optional subject predicate object properties 
selector)
   "Return rows matching SUBJECT, PREDICATE, OBJECT, PROPERTIES.
 If any of these are nil, they are not included in the select
 statement. The SELECTOR is list of symbols subject, precicate,
 object, properties to retrieve or nil for *."
-  (mapcar (lambda (row) (mapcar #'triples-standardize-result row))
-          (sqlite-select db
-                         (concat "SELECT "
-                                 (if selector
-                                     (mapconcat (lambda (e) (format "%s" e)) 
selector ", ")
-                                   "*") " FROM triples"
-                                   (when (or subject predicate object 
properties)
-                                     (concat " WHERE "
-                                             (string-join
-                                              (seq-filter #'identity
-                                                          (list (when subject 
"SUBJECT = ?")
-                                                                (when 
predicate "PREDICATE = ?")
-                                                                (when object 
"OBJECT = ?")
-                                                                (when 
properties "PROPERTIES = ?")))
-                                              " AND "))))
-                         (mapcar #'triples-standardize-val (seq-filter 
#'identity (list subject predicate object properties))))))
+  (pcase (triples--sqlite-interface)
+    ('builtin (mapcar (lambda (row) (mapcar #'triples-standardize-result row))
+                      (sqlite-select db
+                                     (concat "SELECT "
+                                             (if selector
+                                                 (mapconcat (lambda (e) 
(format "%s" e)) selector ", ")
+                                               "*") " FROM triples"
+                                             (when (or subject predicate 
object properties)
+                                               (concat " WHERE "
+                                                       (string-join
+                                                        (seq-filter #'identity
+                                                                    (list 
(when subject "SUBJECT = ?")
+                                                                          
(when predicate "PREDICATE = ?")
+                                                                          
(when object "OBJECT = ?")
+                                                                          
(when properties "PROPERTIES = ?")))
+                                                        " AND "))))
+                                     (mapcar #'triples-standardize-val 
(seq-filter #'identity (list subject predicate object properties))))))
+    ('emacsql (emacsql db (apply #'vector
+                                 (append `(:select
+                                           ,(if selector
+                                                (mapconcat (lambda (e) (format 
"%s" e)) selector ", ")
+                                              '*)
+                                           :from triples)
+                                         (when (or subject predicate object 
properties)
+                                           (triples--emacsql-andify 
+                                            (append
+                                             '(:where)
+                                             (when subject '((= subject $s1)))
+                                             (when predicate '((= predicate 
$r2)))
+                                             (when object '((= object $s3)))
+                                             (when properties '((= properties 
$r4))))))))
+                       (seq-filter #'identity (list subject predicate object 
properties))))))
+
+;; Code after this point should not call sqlite or emacsql directly. If any 
more
+;; calls are needed, put them in a defun, make it work for sqlite and emacsql,
+;; and put them above.
+
+(defun triples--subjects (triples)
+  "Return all unique subjects in TRIPLES."
+  (seq-uniq (mapcar #'car triples)))
+
+(defun triples--group-by-subjects (triples)
+  "Return an alist of subject to TRIPLES with that subject."
+  (let ((subj-to-triples (make-hash-table :test #'equal)))
+    (dolist (triple triples)
+      (puthash (car triple)
+               (cons triple (gethash (car triple) subj-to-triples))
+               subj-to-triples))
+    (cl-loop for k being the hash-keys of subj-to-triples using (hash-values v)
+             collect (cons k v))))
 
 (defun triples--add (db op)
   "Perform OP on DB."



reply via email to

[Prev in Thread] Current Thread [Next in Thread]