debug: db validate

This commit is contained in:
Tienson Qin
2023-06-20 19:25:45 +08:00
parent 8a1c54173b
commit 5445a83b5c
3 changed files with 90 additions and 32 deletions

View File

@@ -511,24 +511,6 @@ independent of format as format specific heading characters are stripped"
(not= parent-id (:db/id node)))
node)) lefts))))
(defn- get-next-outdented-block
"Get the next outdented block of the block that has the `id`.
e.g.
- a
- b
- c
- d
The next outdented block of `c` is `d`."
[db id]
(when-let [block (db-utils/entity db id)]
(let [parent (:block/parent block)]
(if-let [parent-sibling (get-by-parent-&-left db
(:db/id (:block/parent parent))
(:db/id parent))]
parent-sibling
(get-next-outdented-block db (:db/id parent))))))
(defn top-block?
[block]
(= (:db/id (:block/parent block))

View File

@@ -0,0 +1,50 @@
(ns frontend.db.validate
"DB validation.
For pages:
1. Each block should has a unique [:block/parent :block/left] position.
2. For any block, its children should be connected by :block/left."
(:require [datascript.core :as d]
[medley.core :as medley]))
(defn- broken-chain?
[page parent-left->eid]
(let [parents (->> (:block/_page page)
(filter #(seq (:block/_parent %)))
(cons page))]
(some
(fn [parent]
(let [parent-id (:db/id parent)
blocks (:block/_parent parent)]
(when (seq blocks)
(when-let [start (parent-left->eid [parent-id parent-id])]
(let [chain (loop [current start
chain [start]]
(let [next (parent-left->eid [parent-id current])]
(if next
(recur next (conj chain next))
chain)))]
(when (not= (count chain) (count blocks))
{:parent parent
:chain chain
:broken-blocks (remove (set chain) (map :db/id blocks))
:blocks blocks}))))))
parents)))
(defn broken-page?
"Whether `page` is broken."
[db page-id]
(prn :debug " Validate page: " page-id)
(let [parent-left-f (fn [b]
[(get-in b [:block/parent :db/id])
(get-in b [:block/left :db/id])])
page (d/entity db page-id)
blocks (:block/_page page)
parent-left->es (group-by parent-left-f blocks)
conflicted (filter #(> (count (second %)) 1) parent-left->es)]
(if (seq conflicted)
[:conflict-parent-left conflicted]
(let [parent-left->eid (medley/map-vals (fn [c] (:db/id (first c))) parent-left->es)]
(if-let [result (broken-chain? page parent-left->eid)]
[:broken-chain result]
false)))))

View File

@@ -1,19 +1,20 @@
(ns frontend.modules.outliner.datascript
(:require [datascript.core :as d]
[frontend.db.conn :as conn]
[frontend.db :as db]
[frontend.db.react :as react]
[frontend.modules.outliner.pipeline :as pipelines]
[frontend.modules.editor.undo-redo :as undo-redo]
[frontend.state :as state]
[frontend.config :as config]
[logseq.graph-parser.util :as gp-util]
[lambdaisland.glogi :as log]
[frontend.search :as search]
[clojure.string :as string]
[frontend.util :as util]
[frontend.util.property-edit :as property-edit]
[logseq.graph-parser.util.block-ref :as block-ref]))
[frontend.db.conn :as conn]
[frontend.db :as db]
[frontend.db.react :as react]
[frontend.modules.outliner.pipeline :as pipelines]
[frontend.modules.editor.undo-redo :as undo-redo]
[frontend.state :as state]
[frontend.config :as config]
[logseq.graph-parser.util :as gp-util]
[lambdaisland.glogi :as log]
[frontend.search :as search]
[clojure.string :as string]
[frontend.util :as util]
[frontend.util.property-edit :as property-edit]
[logseq.graph-parser.util.block-ref :as block-ref]
[frontend.db.validate :as db-validate]))
(defn new-outliner-txs-state [] (atom []))
@@ -113,6 +114,29 @@
(concat txs retracted-tx'))
txs))
(defn validate-db!
[{:keys [db-before db-after tx-data]}]
(let [changed-pages (->> (filter (fn [d] (contains? #{:block/left :block/parent} (:a d))) tx-data)
(map :e)
distinct
(map (fn [id]
(-> (or (d/entity db-after id)
(d/entity db-before id))
:block/page
:db/id)))
(remove nil?)
(distinct))]
(reduce
(fn [_ page-id]
(if-let [result (db-validate/broken-page? db-after page-id)]
(do
;; revert db changes
(assert (false? result) (str "Broken page: " result))
(reduced false))
true))
true
changed-pages)))
(defn transact!
[txs opts before-editor-cursor]
(let [repo (state/get-current-repo)
@@ -148,6 +172,8 @@
conn (conn/get-db repo false)
rs (d/transact! conn txs (assoc opts :outliner/transact? true))
tx-id (get-tx-id rs)]
;; TODO: disable this when db is stable
(validate-db! rs)
(state/update-state! :history/tx->editor-cursor
(fn [m] (assoc m tx-id before-editor-cursor)))