fix: rename db ident is no longer safe in the new sync

Because Datascript doesn't support multiple db/idents for the same
entity.
This commit is contained in:
Tienson Qin
2026-02-06 20:02:51 +08:00
parent a8ed1c88e6
commit 720432d247
2 changed files with 4 additions and 160 deletions

View File

@@ -5,7 +5,6 @@
[datascript.core :as d]
[datascript.impl.entity :as de]
[frontend.worker-common.util :as worker-util]
[frontend.worker.db.rename-db-ident :as rename-db-ident]
[logseq.common.config :as common-config]
[logseq.common.util :as common-util]
[logseq.db :as ldb]
@@ -18,116 +17,6 @@
;; Frontend migrations
;; ===================
(defn- rename-properties-fix
[db props-to-rename]
(let [;; update property title/name
;; don't update :db/ident since it's addressed by `:rename-db-idents`
property-tx (map
(fn [[old new]]
(merge {:db/id (:db/id (d/entity db old))}
(when-let [new-title (get-in db-property/built-in-properties [new :title])]
{:block/title new-title
:block/name (common-util/page-name-sanity-lc new-title)})))
props-to-rename)
titles-tx (->> (d/datoms db :avet :block/title)
(keep (fn [d]
(let [title (:v d)]
(if (string? title)
(when-let [props (seq (filter (fn [[old _new]] (string/includes? (:v d) (str old))) props-to-rename))]
(let [title' (reduce (fn [title [old new]]
(string/replace title (str old) (str new))) title props)]
[:db/add (:e d) :block/title title']))
[:db/retract (:e d) :block/title])))))
sorting-tx (->> (d/datoms db :avet :logseq.property.table/sorting)
(keep (fn [d]
(when (coll? (:v d))
(when-let [props (seq (filter (fn [[old _new]]
(some (fn [item] (= old (:id item))) (:v d))) props-to-rename))]
(let [value (reduce
(fn [sorting [old new]]
(mapv
(fn [item]
(if (= old (:id item))
(assoc item :id new)
item))
sorting))
(:v d)
props)]
[:db/add (:e d) :logseq.property.table/sorting value]))))))
sized-columns-tx (->> (d/datoms db :avet :logseq.property.table/sized-columns)
(keep (fn [d]
(when (map? (:v d))
(when-let [props (seq (filter (fn [[old _new]] (get (:v d) old)) props-to-rename))]
(let [value (reduce
(fn [sizes [old new]]
(if-let [size (get sizes old)]
(-> sizes
(dissoc old)
(assoc new size))
sizes))
(:v d)
props)]
[:db/add (:e d) :logseq.property.table/sized-columns value]))))))
hidden-columns-tx (mapcat
(fn [[old new]]
(->> (d/datoms db :avet :logseq.property.table/hidden-columns old)
(mapcat (fn [d]
[[:db/retract (:e d) :logseq.property.table/hidden-columns old]
[:db/add (:e d) :logseq.property.table/hidden-columns new]]))))
props-to-rename)
ordered-columns-tx (->> (d/datoms db :avet :logseq.property.table/ordered-columns)
(keep (fn [d]
(when (coll? (:v d))
(when-let [props (seq (filter (fn [[old _new]] ((set (:v d)) old)) props-to-rename))]
(let [value (reduce
(fn [col [old new]]
(mapv (fn [v] (if (= old v) new v)) col))
(:v d)
props)]
[:db/add (:e d) :logseq.property.table/ordered-columns value]))))))
filters-tx (->> (d/datoms db :avet :logseq.property.table/filters)
(keep (fn [d]
(let [filters (:filters (:v d))]
(when (coll? filters)
(when-let [props (seq (filter (fn [[old _new]]
(some (fn [item] (and (vector? item)
(= old (first item)))) filters)) props-to-rename))]
(let [value (update (:v d) :filters
(fn [col]
(reduce
(fn [col [old new]]
(mapv (fn [item]
(if (and (vector? item) (= old (first item)))
(vec (cons new (rest item)))
item))
col))
col
props)))]
[:db/add (:e d) :logseq.property.table/filters value])))))))]
(concat property-tx
titles-tx
sorting-tx
sized-columns-tx
hidden-columns-tx
ordered-columns-tx
filters-tx)))
(defn- rename-properties
[props-to-rename {:keys [fix]}]
{:rename-db-idents (fn [_db]
(mapv
(fn [[old-ident new-ident]]
{:db-ident-or-block-uuid old-ident
:new-db-ident new-ident})
props-to-rename))
:fix (fn [db]
(let [common-fix (rename-properties-fix db
{:logseq.property.asset/external-src
:logseq.property.asset/external-url})
additional-fix (when (fn? fix)
(fix db))]
(concat common-fix additional-fix)))})
(defn- add-quick-add-page
[_db]
(let [page (-> (-> (sqlite-util/build-new-page common-config/quick-add-page-name)
@@ -169,7 +58,7 @@
(def schema-version->updates
"A vec of tuples defining datascript migrations. Each tuple consists of the
schema version integer and a migration map. A migration map can have keys of :properties, :classes
:rename-db-idents and :fix."
and :fix."
[["65.7" {:fix add-quick-add-page}]
["65.8" {:fix add-missing-page-name}]
["65.9" {:properties [:logseq.property.embedding/hnsw-label-updated-at]}]
@@ -179,9 +68,6 @@
["65.13" {:properties [:logseq.property.asset/width
:logseq.property.asset/height]}]
["65.14" {:properties [:logseq.property.asset/external-src]}]
["65.15" (rename-properties {:logseq.property.asset/external-src
:logseq.property.asset/external-url}
{})]
["65.16" {:properties [:logseq.property.asset/external-file-name]}]
["65.17" {:properties [:logseq.property.publish/published-url]}]
["65.18" {:fix deprecated-ensure-graph-uuid}]
@@ -281,7 +167,7 @@
(defn- upgrade-version!
"Return tx-data"
[conn version {:keys [properties classes rename-db-idents fix] :as migrate-updates}]
[conn version {:keys [properties classes fix] :as migrate-updates}]
(let [version (db-schema/parse-schema-version version)
db @conn
new-properties (->> (select-keys db-property/built-in-properties properties)
@@ -303,19 +189,14 @@
new-class-idents (keep (fn [class]
(when-let [db-ident (:db/ident class)]
{:db/ident db-ident})) new-classes)
[rename-db-idents-tx-data rename-db-idents-coll]
(when rename-db-idents
(rename-db-ident/rename-db-idents-migration-tx-data db rename-db-idents))
fixes (when (fn? fix)
(fix db))
tx-data (concat new-class-idents new-properties new-classes rename-db-idents-tx-data fixes)
tx-data (concat new-class-idents new-properties new-classes fixes)
tx-data' (concat
[(sqlite-util/kv :logseq.kv/schema-version version)]
tx-data)
r (ldb/transact! conn tx-data' {:db-migrate? true
:skip-validate-db? true})
migrate-updates (cond-> migrate-updates
rename-db-idents (assoc :rename-db-idents rename-db-idents-coll))]
:skip-validate-db? true})]
(println "DB schema migrated to" version)
(assoc r :migrate-updates migrate-updates)))

View File

@@ -1,37 +0,0 @@
(ns frontend.worker.db.rename-db-ident
"utils for rename-db-idents migration"
(:require [datascript.core :as d]
[logseq.db :as ldb]))
(defn rename-db-idents-migration-tx-data
"Rename :db/ident and replace all usages as well.
rename-db-idents: fn to generate coll of {:db-ident ..., :new-db-ident ...}
NOTE: this fn should only care about :db/ident changing, don't touch other attr/values"
[db rename-db-idents]
(assert (fn? rename-db-idents))
(let [rename-db-idents-coll (rename-db-idents db)
rename-db-idents-coll2 (rename-db-idents db)
;; ensure there's no random data in result
_ (when (not= rename-db-idents-coll rename-db-idents-coll2)
(throw (ex-info "db-idents cannot be randomly generated"
{:rename-db-idents-colls [rename-db-idents-coll rename-db-idents-coll2]})))
*rename-db-idents-coll (atom [])
tx-data
(->> (for [{:keys [db-ident-or-block-uuid new-db-ident] :as rename-db-ident} rename-db-idents-coll
:let [ent (d/entity db (if (keyword? db-ident-or-block-uuid)
db-ident-or-block-uuid
[:block/uuid db-ident-or-block-uuid]))
old-db-ident (:db/ident ent)]]
(do (when (some? ent)
(when-not (or (ldb/class? ent) (ldb/property? ent))
(throw (ex-info "Only entities of class or property type support :rename-db-ident" {:ent (into {} ent)})))
(swap! *rename-db-idents-coll conj rename-db-ident))
(cons {:db/id (:db/id ent) :db/ident new-db-ident}
(some->> old-db-ident
(d/q '[:find ?b ?v :in $ ?a :where [?b ?a ?v]] db)
(mapcat (fn [[id v]]
[[:db/retract id old-db-ident]
[:db/add id new-db-ident v]]))))))
(apply concat)
doall)]
[tx-data @*rename-db-idents-coll]))