|
|
@@ -32,7 +32,6 @@
|
|
|
[frontend.worker.util :as worker-util]
|
|
|
[goog.object :as gobj]
|
|
|
[lambdaisland.glogi.console :as glogi-console]
|
|
|
- [logseq.common.log :as log]
|
|
|
[logseq.common.util :as common-util]
|
|
|
[logseq.db :as ldb]
|
|
|
[logseq.db.common.entity-plus :as entity-plus]
|
|
|
@@ -101,38 +100,39 @@
|
|
|
[^js pool data]
|
|
|
(.importDb ^js pool repo-path data))
|
|
|
|
|
|
-(defn- get-all-datoms-from-sqlite-db
|
|
|
- [db]
|
|
|
- (some->> (.exec db #js {:sql "select * from kvs"
|
|
|
- :rowMode "array"})
|
|
|
- bean/->clj
|
|
|
- (mapcat
|
|
|
- (fn [[_addr content _addresses]]
|
|
|
- (let [content' (sqlite-util/transit-read content)
|
|
|
- datoms (when (map? content')
|
|
|
- (:keys content'))]
|
|
|
- datoms)))
|
|
|
- distinct
|
|
|
- (map (fn [[e a v t]]
|
|
|
- (d/datom e a v t)))))
|
|
|
-
|
|
|
-(defn- rebuild-db-from-datoms!
|
|
|
- "Persistent-sorted-set has been broken, used addresses can't be found"
|
|
|
- [datascript-conn sqlite-db import-type]
|
|
|
- (let [datoms (get-all-datoms-from-sqlite-db sqlite-db)
|
|
|
- db (d/init-db [] db-schema/schema
|
|
|
- {:storage (storage/storage @datascript-conn)})
|
|
|
- db (d/db-with db
|
|
|
- (map (fn [d]
|
|
|
- [:db/add (:e d) (:a d) (:v d) (:t d)]) datoms))]
|
|
|
- (prn :debug :rebuild-db-from-datoms :datoms-count (count datoms))
|
|
|
+(comment
|
|
|
+ (defn- get-all-datoms-from-sqlite-db
|
|
|
+ [db]
|
|
|
+ (some->> (.exec db #js {:sql "select * from kvs"
|
|
|
+ :rowMode "array"})
|
|
|
+ bean/->clj
|
|
|
+ (mapcat
|
|
|
+ (fn [[_addr content _addresses]]
|
|
|
+ (let [content' (sqlite-util/transit-read content)
|
|
|
+ datoms (when (map? content')
|
|
|
+ (:keys content'))]
|
|
|
+ datoms)))
|
|
|
+ distinct
|
|
|
+ (map (fn [[e a v t]]
|
|
|
+ (d/datom e a v t)))))
|
|
|
+
|
|
|
+ (defn- rebuild-db-from-datoms!
|
|
|
+ "Persistent-sorted-set has been broken, used addresses can't be found"
|
|
|
+ [datascript-conn sqlite-db import-type]
|
|
|
+ (let [datoms (get-all-datoms-from-sqlite-db sqlite-db)
|
|
|
+ db (d/init-db [] db-schema/schema
|
|
|
+ {:storage (storage/storage @datascript-conn)})
|
|
|
+ db (d/db-with db
|
|
|
+ (map (fn [d]
|
|
|
+ [:db/add (:e d) (:a d) (:v d) (:t d)]) datoms))]
|
|
|
+ (prn :debug :rebuild-db-from-datoms :datoms-count (count datoms))
|
|
|
;; export db first
|
|
|
- (when-not import-type
|
|
|
- (worker-util/post-message :notification ["The SQLite db will be exported to avoid any data-loss." :warning false])
|
|
|
- (worker-util/post-message :export-current-db []))
|
|
|
- (.exec sqlite-db #js {:sql "delete from kvs"})
|
|
|
- (d/reset-conn! datascript-conn db)
|
|
|
- (db-migrate/fix-db! datascript-conn)))
|
|
|
+ (when-not import-type
|
|
|
+ (worker-util/post-message :notification ["The SQLite db will be exported to avoid any data-loss." :warning false])
|
|
|
+ (worker-util/post-message :export-current-db []))
|
|
|
+ (.exec sqlite-db #js {:sql "delete from kvs"})
|
|
|
+ (d/reset-conn! datascript-conn db)
|
|
|
+ (db-migrate/fix-db! datascript-conn))))
|
|
|
|
|
|
(comment
|
|
|
(defn- gc-kvs-table!
|
|
|
@@ -158,104 +158,74 @@
|
|
|
:bind #js [addr]}))))))))
|
|
|
|
|
|
(defn- find-missing-addresses
|
|
|
- [^Object db & {:keys [delete-addrs upsert-addr-content? open-db?]}]
|
|
|
- (worker-util/profile
|
|
|
- "find-missing-addresses"
|
|
|
- (let [schema (some->> (.exec db #js {:sql "select content from kvs where addr = 0"
|
|
|
- :rowMode "array"})
|
|
|
- bean/->clj
|
|
|
- ffirst
|
|
|
- sqlite-util/transit-read)
|
|
|
- result (->> (.exec db #js {:sql "select addr, addresses from kvs"
|
|
|
- :rowMode "array"})
|
|
|
- bean/->clj
|
|
|
- (keep (fn [[addr addresses]]
|
|
|
- (when-not (and delete-addrs (delete-addrs addr))
|
|
|
- [addr (bean/->clj (js/JSON.parse addresses))]))))
|
|
|
- used-addresses (-> (set (concat (mapcat second result)
|
|
|
- [0 1 (:eavt schema) (:avet schema) (:aevt schema)]))
|
|
|
- (clojure.set/difference delete-addrs))
|
|
|
- missing-addresses (clojure.set/difference used-addresses (set (map first result)))]
|
|
|
- (when (seq missing-addresses)
|
|
|
- (prn :error :missing-addresses missing-addresses)
|
|
|
- (if worker-util/dev?
|
|
|
- (throw (ex-info "Found missing addresses that shouldn't happen" {:missing-addresses missing-addresses}))
|
|
|
- (worker-util/post-message :capture-error
|
|
|
- {:error "v2-db-missing-addresses"
|
|
|
- :payload {:missing-addresses missing-addresses
|
|
|
- :upsert-addr-content upsert-addr-content?
|
|
|
- :open-db open-db?}})))
|
|
|
- missing-addresses)))
|
|
|
+ [conn ^Object db & {:keys [delete-addrs]}]
|
|
|
+ (let [schema (some->> (.exec db #js {:sql "select content from kvs where addr = 0"
|
|
|
+ :rowMode "array"})
|
|
|
+ bean/->clj
|
|
|
+ ffirst
|
|
|
+ sqlite-util/transit-read)
|
|
|
+ result (->> (.exec db #js {:sql "select addr, addresses from kvs"
|
|
|
+ :rowMode "array"})
|
|
|
+ bean/->clj
|
|
|
+ (keep (fn [[addr addresses]]
|
|
|
+ (when-not (and delete-addrs (delete-addrs addr))
|
|
|
+ [addr (bean/->clj (js/JSON.parse addresses))]))))
|
|
|
+ used-addresses (-> (set (concat (mapcat second result)
|
|
|
+ [0 1 (:eavt schema) (:avet schema) (:aevt schema)]))
|
|
|
+ (clojure.set/difference delete-addrs))
|
|
|
+ missing-addresses (clojure.set/difference used-addresses (set (map first result)))]
|
|
|
+ (when (seq missing-addresses)
|
|
|
+ (let [version-in-db (when conn (db-schema/parse-schema-version (or (:kv/value (d/entity @conn :logseq.kv/schema-version)) 0)))
|
|
|
+ compare-result (when version-in-db (db-schema/compare-schema-version version-in-db "64.8"))]
|
|
|
+ (when (and compare-result (not (neg? compare-result))) ; >= 64.8
|
|
|
+ (worker-util/post-message :capture-error
|
|
|
+ {:error "db-missing-addresses-v2"
|
|
|
+ :payload {:missing-addresses missing-addresses}}))))
|
|
|
+ missing-addresses))
|
|
|
|
|
|
(defn upsert-addr-content!
|
|
|
"Upsert addr+data-seq. Update sqlite-cli/upsert-addr-content! when making changes"
|
|
|
- [repo data delete-addrs* & {:keys [client-ops-db?] :or {client-ops-db? false}}]
|
|
|
- (let [^Object db (worker-state/get-sqlite-conn repo (if client-ops-db? :client-ops :db))
|
|
|
- delete-addrs (set delete-addrs*)]
|
|
|
+ [db data delete-addrs*]
|
|
|
+ (let [delete-addrs (clojure.set/difference (set delete-addrs*) #{0 1})]
|
|
|
(assert (some? db) "sqlite db not exists")
|
|
|
(.transaction db (fn [tx]
|
|
|
(doseq [item data]
|
|
|
(.exec tx #js {:sql "INSERT INTO kvs (addr, content, addresses) values ($addr, $content, $addresses) on conflict(addr) do update set content = $content, addresses = $addresses"
|
|
|
:bind item}))))
|
|
|
(when (seq delete-addrs)
|
|
|
+ (.transaction db (fn [tx]
|
|
|
+ (doseq [addr delete-addrs]
|
|
|
+ (.exec tx #js {:sql "Delete from kvs WHERE addr = ? AND NOT EXISTS (SELECT 1 FROM json_each(addresses) WHERE value = ?);"
|
|
|
+ :bind #js [addr]}))))
|
|
|
(let [missing-addrs (when worker-util/dev?
|
|
|
- (seq (find-missing-addresses db {:delete-addrs delete-addrs
|
|
|
- :upsert-addr-content? true})))
|
|
|
- delete-addrs' (if missing-addrs
|
|
|
- (remove (set missing-addrs) delete-addrs)
|
|
|
- delete-addrs)]
|
|
|
- (when (seq delete-addrs')
|
|
|
- (.transaction db (fn [tx]
|
|
|
- (doseq [addr delete-addrs']
|
|
|
- (.exec tx #js {:sql "Delete from kvs WHERE addr = ? AND NOT EXISTS (SELECT 1 FROM json_each(addresses) WHERE value = ?);"
|
|
|
- :bind #js [addr]})))))))))
|
|
|
+ (seq (find-missing-addresses nil db {:delete-addrs delete-addrs})))]
|
|
|
+ (if (seq missing-addrs)
|
|
|
+ (worker-util/post-message :notification [(str "Bug!! Missing addresses: " missing-addrs) :error false])
|
|
|
+ (when (seq delete-addrs)
|
|
|
+ (.transaction db (fn [tx]
|
|
|
+ (doseq [addr delete-addrs]
|
|
|
+ (.exec tx #js {:sql "Delete from kvs WHERE addr = ? AND NOT EXISTS (SELECT 1 FROM json_each(addresses) WHERE value = ?);"
|
|
|
+ :bind #js [addr]}))))))))))
|
|
|
|
|
|
(defn restore-data-from-addr
|
|
|
"Update sqlite-cli/restore-data-from-addr when making changes"
|
|
|
- [repo addr & {:keys [client-ops-db?] :or {client-ops-db? false}}]
|
|
|
- (let [^Object db (worker-state/get-sqlite-conn repo (if client-ops-db? :client-ops :db))]
|
|
|
- (assert (some? db) "sqlite db not exists")
|
|
|
- (when-let [result (-> (.exec db #js {:sql "select content, addresses from kvs where addr = ?"
|
|
|
- :bind #js [addr]
|
|
|
- :rowMode "array"})
|
|
|
- first)]
|
|
|
- (let [[content addresses] (bean/->clj result)
|
|
|
- addresses (when addresses
|
|
|
- (js/JSON.parse addresses))
|
|
|
- data (sqlite-util/transit-read content)]
|
|
|
- (if (and addresses (map? data))
|
|
|
- (assoc data :addresses addresses)
|
|
|
- data)))))
|
|
|
+ [db addr]
|
|
|
+ (assert (some? db) "sqlite db not exists")
|
|
|
+ (when-let [result (-> (.exec db #js {:sql "select content, addresses from kvs where addr = ?"
|
|
|
+ :bind #js [addr]
|
|
|
+ :rowMode "array"})
|
|
|
+ first)]
|
|
|
+ (let [[content addresses] (bean/->clj result)
|
|
|
+ addresses (when addresses
|
|
|
+ (js/JSON.parse addresses))
|
|
|
+ data (sqlite-util/transit-read content)]
|
|
|
+ (if (and addresses (map? data))
|
|
|
+ (assoc data :addresses addresses)
|
|
|
+ data))))
|
|
|
|
|
|
(defn new-sqlite-storage
|
|
|
"Update sqlite-cli/new-sqlite-storage when making changes"
|
|
|
- [repo _opts]
|
|
|
- (reify IStorage
|
|
|
- (-store [_ addr+data-seq delete-addrs]
|
|
|
- (let [used-addrs (set (mapcat
|
|
|
- (fn [[addr data]]
|
|
|
- (cons addr
|
|
|
- (when (map? data)
|
|
|
- (:addresses data))))
|
|
|
- addr+data-seq))
|
|
|
- delete-addrs (remove used-addrs delete-addrs)
|
|
|
- data (map
|
|
|
- (fn [[addr data]]
|
|
|
- (let [data' (if (map? data) (dissoc data :addresses) data)
|
|
|
- addresses (when (map? data)
|
|
|
- (when-let [addresses (:addresses data)]
|
|
|
- (js/JSON.stringify (bean/->js addresses))))]
|
|
|
- #js {:$addr addr
|
|
|
- :$content (sqlite-util/transit-write data')
|
|
|
- :$addresses addresses}))
|
|
|
- addr+data-seq)]
|
|
|
- (upsert-addr-content! repo data delete-addrs)))
|
|
|
-
|
|
|
- (-restore [_ addr]
|
|
|
- (restore-data-from-addr repo addr))))
|
|
|
-
|
|
|
-(defn new-sqlite-client-ops-storage
|
|
|
- [repo]
|
|
|
+ [^Object db]
|
|
|
(reify IStorage
|
|
|
(-store [_ addr+data-seq delete-addrs]
|
|
|
(let [used-addrs (set (mapcat
|
|
|
@@ -275,10 +245,10 @@
|
|
|
:$content (sqlite-util/transit-write data')
|
|
|
:$addresses addresses}))
|
|
|
addr+data-seq)]
|
|
|
- (upsert-addr-content! repo data delete-addrs :client-ops-db? true)))
|
|
|
+ (upsert-addr-content! db data delete-addrs)))
|
|
|
|
|
|
(-restore [_ addr]
|
|
|
- (restore-data-from-addr repo addr :client-ops-db? true))))
|
|
|
+ (restore-data-from-addr db addr))))
|
|
|
|
|
|
(defn- close-db-aux!
|
|
|
[repo ^Object db ^Object search ^Object client-ops]
|
|
|
@@ -338,8 +308,9 @@
|
|
|
[repo {:keys [config import-type datoms]}]
|
|
|
(when-not (worker-state/get-sqlite-conn repo)
|
|
|
(p/let [[db search-db client-ops-db :as dbs] (get-dbs repo)
|
|
|
- storage (new-sqlite-storage repo {})
|
|
|
- client-ops-storage (when-not @*publishing? (new-sqlite-client-ops-storage repo))
|
|
|
+ storage (new-sqlite-storage db)
|
|
|
+ client-ops-storage (when-not @*publishing?
|
|
|
+ (new-sqlite-storage client-ops-db))
|
|
|
db-based? (sqlite-util/db-based-graph? repo)]
|
|
|
(swap! *sqlite-conns assoc repo {:db db
|
|
|
:search search-db
|
|
|
@@ -374,24 +345,14 @@
|
|
|
(when import-type {:import-type import-type}))]
|
|
|
(d/transact! conn initial-data {:initial-db? true})))
|
|
|
|
|
|
- (try
|
|
|
-
|
|
|
- ;; TODO: remove this once we can ensure there's no bug for missing addresses
|
|
|
- ;; because it's slow for large graphs
|
|
|
- (when-not import-type
|
|
|
- (when-let [missing-addresses (seq (find-missing-addresses db {:open-db? true}))]
|
|
|
- (throw (ex-info "DB missing addresses" {:missing-addresses missing-addresses}))))
|
|
|
-
|
|
|
- (db-migrate/migrate conn search-db)
|
|
|
+ ;; TODO: remove this once we can ensure there's no bug for missing addresses
|
|
|
+ ;; because it's slow for large graphs
|
|
|
+ (when-not import-type
|
|
|
+ (when-let [missing-addresses (seq (find-missing-addresses conn db))]
|
|
|
+ (worker-util/post-message :notification ["It seems that the DB has been broken, please export a backup and contact Logseq team for help." :error false])
|
|
|
+ (throw (ex-info "DB missing addresses" {:missing-addresses missing-addresses}))))
|
|
|
|
|
|
- ;; TODO: Remove this once we can ensure there's no bug for missing addresses
|
|
|
- (catch :default e
|
|
|
- (log/error (str "DB migrate failed for " repo ", error:") e)
|
|
|
- (if (= (.-message e) "DB missing addresses")
|
|
|
- (do
|
|
|
- (rebuild-db-from-datoms! conn db import-type)
|
|
|
- (db-migrate/migrate conn search-db))
|
|
|
- (throw e))))
|
|
|
+ (db-migrate/migrate conn search-db)
|
|
|
|
|
|
(db-listener/listen-db-changes! repo (get @*datascript-conns repo))))))
|
|
|
|