|
|
@@ -1,63 +1,70 @@
|
|
|
(ns frontend.db-worker
|
|
|
"Worker used for browser DB implementation"
|
|
|
- (:require ["@logseq/sqlite" :as sqlite-db :default wasm-bindgen-init]
|
|
|
- ["comlink" :as Comlink]
|
|
|
- [promesa.core :as p]
|
|
|
+ (:require [promesa.core :as p]
|
|
|
[datascript.storage :refer [IStorage]]
|
|
|
[cljs.cache :as cache]
|
|
|
- [cljs.reader :as reader]
|
|
|
+ [clojure.edn :as edn]
|
|
|
[datascript.core :as d]
|
|
|
[logseq.db.frontend.schema :as db-schema]
|
|
|
[shadow.cljs.modern :refer [defclass]]
|
|
|
[datascript.transit :as dt]
|
|
|
- [clojure.edn :as edn]
|
|
|
- [clojure.string :as string]
|
|
|
- ["@logseq/sqlite-wasm" :as sqlite-wasm]))
|
|
|
-
|
|
|
-(def *wasm-loaded (atom false))
|
|
|
+ ["@logseq/sqlite-wasm" :default sqlite3InitModule]
|
|
|
+ ["comlink" :as Comlink]))
|
|
|
|
|
|
-;; datascript conns
|
|
|
-(defonce conns (atom nil))
|
|
|
+(defonce *sqlite (atom nil))
|
|
|
+(defonce *sqlite-db (atom nil))
|
|
|
+(defonce *datascript-conn (atom nil))
|
|
|
|
|
|
-(defn get-conn
|
|
|
- [repo]
|
|
|
- (get @conns repo))
|
|
|
+(defn- init-sqlite-module!
|
|
|
+ []
|
|
|
+ (when-not @*sqlite
|
|
|
+ (p/let [base-url (str js/self.location.protocol "//" js/self.location.host)
|
|
|
+ sqlite-wasm-url (str base-url "/js/")
|
|
|
+ sqlite (sqlite3InitModule (clj->js {:url sqlite-wasm-url
|
|
|
+ :print js/console.log
|
|
|
+ :printErr js/console.error}))]
|
|
|
+ (reset! *sqlite sqlite))))
|
|
|
+
|
|
|
+(defn- close-all-dbs!
|
|
|
+ []
|
|
|
+ )
|
|
|
|
|
|
(defn upsert-addr-content!
|
|
|
"Upsert addr+data-seq"
|
|
|
- [repo data]
|
|
|
- (.upsert_addr_content sqlite-db repo data))
|
|
|
+ [data]
|
|
|
+ (assert (some? @*sqlite-db) "sqlite db not exists")
|
|
|
+ (let [^Object db @*sqlite-db]
|
|
|
+ (.transaction db (fn [tx]
|
|
|
+ (doseq [item data]
|
|
|
+ (.exec tx #js {:sql "INSERT INTO kvs (addr, content) values ($addr, $content) on conflict(addr) do update set content = $content"
|
|
|
+ :bind item}))))))
|
|
|
|
|
|
(defn restore-data-from-addr
|
|
|
- [repo addr]
|
|
|
- (.get_content_by_addr sqlite-db repo addr))
|
|
|
-
|
|
|
+ [addr]
|
|
|
+ (assert (some? @*sqlite-db) "sqlite db not exists")
|
|
|
+ (when-let [content (-> (.exec @*sqlite-db #js {:sql "select content from kvs where addr = ?"
|
|
|
+ :bind #js [addr]
|
|
|
+ :rowMode "array"})
|
|
|
+ ffirst)]
|
|
|
+ (edn/read-string content)))
|
|
|
|
|
|
(defn new-sqlite-storage
|
|
|
- [repo {:keys [threshold]
|
|
|
- :or {threshold 4096}}]
|
|
|
+ [_repo {:keys [threshold]
|
|
|
+ :or {threshold 4096}}]
|
|
|
(let [_cache (cache/lru-cache-factory {} :threshold threshold)]
|
|
|
(reify IStorage
|
|
|
(-store [_ addr+data-seq]
|
|
|
- (let [data (->>
|
|
|
- (map
|
|
|
- (fn [[addr data]]
|
|
|
- #js {:addr addr
|
|
|
- :content (pr-str data)})
|
|
|
- addr+data-seq)
|
|
|
- (to-array))]
|
|
|
- (upsert-addr-content! repo data)
|
|
|
- {:result "ok"}))
|
|
|
+ (let [data (map
|
|
|
+ (fn [[addr data]]
|
|
|
+ #js {:$addr addr
|
|
|
+ :$content (pr-str data)})
|
|
|
+ addr+data-seq)]
|
|
|
+ (upsert-addr-content! data)))
|
|
|
|
|
|
(-restore [_ addr]
|
|
|
- (let [content (restore-data-from-addr repo addr)]
|
|
|
+ (let [content (restore-data-from-addr addr)]
|
|
|
(edn/read-string content))))))
|
|
|
|
|
|
-(defn split-last [pattern s]
|
|
|
- (when-let [last-index (string/last-index-of s pattern)]
|
|
|
- [(subs s 0 last-index)
|
|
|
- (subs s (+ last-index (count pattern)) (count s))]))
|
|
|
-
|
|
|
#_:clj-kondo/ignore
|
|
|
(defclass SQLiteDB
|
|
|
(extends js/Object)
|
|
|
@@ -67,121 +74,69 @@
|
|
|
(super))
|
|
|
|
|
|
Object
|
|
|
- (init
|
|
|
- [_this]
|
|
|
- (let [[_ sqlite-wasm-url] (split-last "url=" (.. js/location -href))]
|
|
|
- (assert (some? sqlite-wasm-url) "sqlite-wasm-url is empty")
|
|
|
- (p/let [wasm-url (js/URL. sqlite-wasm-url (.. js/location -href))
|
|
|
- _ (wasm-bindgen-init wasm-url)]
|
|
|
- (prn ::init-ok
|
|
|
- :has-opfs-support (.has_opfs_support sqlite-db)
|
|
|
- :sqlite-version (.get_version sqlite-db))
|
|
|
- (reset! *wasm-loaded true))))
|
|
|
|
|
|
- (inited
|
|
|
- [_this]
|
|
|
- (boolean @*wasm-loaded))
|
|
|
+ ;; ;; dev-only, close all db connections and db files
|
|
|
+ ;; (unsafeDevCloseAll
|
|
|
+ ;; [_this]
|
|
|
+ ;; (.dev_close sqlite-db))
|
|
|
|
|
|
- ;; dev-only, close all db connections and db files
|
|
|
- (unsafeDevCloseAll
|
|
|
- [_this]
|
|
|
- (.dev_close sqlite-db))
|
|
|
+ ;; (getVersion
|
|
|
+ ;; [_this]
|
|
|
+ ;; (.get_version sqlite-db))
|
|
|
|
|
|
- (getVersion
|
|
|
+ ;; (supportOPFS
|
|
|
+ ;; [_this]
|
|
|
+ ;; (.has_opfs_support sqlite-db))
|
|
|
+
|
|
|
+ (init
|
|
|
[_this]
|
|
|
- (.get_version sqlite-db))
|
|
|
+ (init-sqlite-module!)
|
|
|
+ nil)
|
|
|
|
|
|
- (supportOPFS
|
|
|
+ (inited
|
|
|
[_this]
|
|
|
- (.has_opfs_support sqlite-db))
|
|
|
+ (some? @*sqlite))
|
|
|
|
|
|
(listDB
|
|
|
[_this]
|
|
|
- (.list_db sqlite-db))
|
|
|
+ ;; (.list_db sqlite-db)
|
|
|
+ #js [])
|
|
|
|
|
|
(newDB
|
|
|
[_this repo]
|
|
|
- (p/do!
|
|
|
- (.ensure_init sqlite-db)
|
|
|
- (.init_db sqlite-db repo) ;; close another and init this one
|
|
|
- (.new_db sqlite-db repo)
|
|
|
- (let [db-name repo
|
|
|
- storage (new-sqlite-storage db-name {})
|
|
|
- conn (or (d/restore-conn storage)
|
|
|
- (d/create-conn db-schema/schema-for-db-based-graph {:storage storage}))]
|
|
|
- (swap! conns assoc db-name conn)
|
|
|
- nil)))
|
|
|
+ ;; TODO: close all the other db connections
|
|
|
+ (p/let [sqlite @*sqlite
|
|
|
+ db-name repo
|
|
|
+ pool (.installOpfsSAHPoolVfs sqlite #js {:name db-name})
|
|
|
+ db (new (.-OpfsSAHPoolDb pool) "/logseq")
|
|
|
+ storage (new-sqlite-storage db-name {})]
|
|
|
+ (reset! *sqlite-db db)
|
|
|
+ (.exec db "PRAGMA locking_mode=exclusive")
|
|
|
+ (.exec db "create table if not exists kvs (addr INTEGER primary key, content TEXT)")
|
|
|
+ (let [conn (or (d/restore-conn storage)
|
|
|
+ (d/create-conn db-schema/schema-for-db-based-graph {:storage storage}))]
|
|
|
+ (reset! *datascript-conn conn)
|
|
|
+ nil)))
|
|
|
|
|
|
(transact
|
|
|
[_this repo tx-data tx-meta]
|
|
|
- (when-let [conn (get-conn repo)]
|
|
|
+ (when-let [conn @*datascript-conn]
|
|
|
(try
|
|
|
- (let [tx-data (reader/read-string tx-data)
|
|
|
- tx-meta (reader/read-string tx-meta)]
|
|
|
- (d/transact! conn tx-data tx-meta))
|
|
|
+ (let [tx-data (edn/read-string tx-data)
|
|
|
+ tx-meta (edn/read-string tx-meta)]
|
|
|
+ (d/transact! conn tx-data tx-meta)
|
|
|
+ nil)
|
|
|
(catch :default e
|
|
|
(prn :debug :error)
|
|
|
(js/console.error e)))))
|
|
|
|
|
|
(getInitialData
|
|
|
[_this repo]
|
|
|
- (when-let [conn (get-conn repo)]
|
|
|
+ (when-let [conn @*datascript-conn]
|
|
|
(let [db @conn]
|
|
|
(->> (d/datoms db :eavt)
|
|
|
- ;; (remove (fn [e] (= :block/content (:a e))))
|
|
|
vec
|
|
|
- dt/write-transit-str))))
|
|
|
-
|
|
|
- (openDB
|
|
|
- [_this repo]
|
|
|
- (p/do!
|
|
|
- (.ensure_init sqlite-db)
|
|
|
- ;; close another and init this one
|
|
|
- (.init_db sqlite-db repo)))
|
|
|
-
|
|
|
- (deleteBlocks
|
|
|
- [_this repo uuids]
|
|
|
- (when (seq uuids)
|
|
|
- (p/do!
|
|
|
- (.ensure_init sqlite-db)
|
|
|
- (.delete_blocks sqlite-db repo uuids))))
|
|
|
-
|
|
|
- (upsertBlocks
|
|
|
- [_this repo blocks]
|
|
|
- (p/do!
|
|
|
- (.ensure_init sqlite-db)
|
|
|
- (.upsert_blocks sqlite-db repo blocks)))
|
|
|
-
|
|
|
- (fetchAllPages
|
|
|
- [_this repo]
|
|
|
- (p/do!
|
|
|
- (.ensure_init sqlite-db)
|
|
|
- (.fetch_all_pages sqlite-db repo)))
|
|
|
-
|
|
|
- ;; fetch all blocks, return block id and page id
|
|
|
- (fetchAllBlocks
|
|
|
- [_this repo]
|
|
|
- (p/do!
|
|
|
- (.ensure_init sqlite-db)
|
|
|
- (.fetch_all_blocks sqlite-db repo)))
|
|
|
-
|
|
|
- (fetchRecentJournals
|
|
|
- [_this repo]
|
|
|
- (p/do!
|
|
|
- (.ensure_init sqlite-db)
|
|
|
- (.fetch_recent_journals sqlite-db repo)))
|
|
|
-
|
|
|
- (fetchInitData
|
|
|
- [_this repo]
|
|
|
- (p/do!
|
|
|
- (.ensure_init sqlite-db)
|
|
|
- (.fetch_init_data sqlite-db repo)))
|
|
|
-
|
|
|
- (fetchBlocksExcluding
|
|
|
- [_this repo excluding-uuids]
|
|
|
- (p/do!
|
|
|
- (.ensure_init sqlite-db)
|
|
|
- (.fetch_blocks_excluding sqlite-db repo excluding-uuids))))
|
|
|
+ dt/write-transit-str)))))
|
|
|
|
|
|
(defn init
|
|
|
"web worker entry"
|