database.go 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516
  1. // SiYuan - Refactor your thinking
  2. // Copyright (c) 2020-present, b3log.org
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. package sql
  17. import (
  18. "bytes"
  19. "crypto/sha256"
  20. "database/sql"
  21. "errors"
  22. "fmt"
  23. "os"
  24. "path/filepath"
  25. "regexp"
  26. "runtime"
  27. "runtime/debug"
  28. "strconv"
  29. "strings"
  30. "sync"
  31. "text/template"
  32. "time"
  33. "unicode/utf8"
  34. "github.com/88250/gulu"
  35. "github.com/88250/lute/ast"
  36. "github.com/88250/lute/html"
  37. "github.com/88250/lute/parse"
  38. "github.com/mattn/go-sqlite3"
  39. _ "github.com/mattn/go-sqlite3"
  40. "github.com/siyuan-note/eventbus"
  41. "github.com/siyuan-note/logging"
  42. "github.com/siyuan-note/siyuan/kernel/treenode"
  43. "github.com/siyuan-note/siyuan/kernel/util"
  44. )
  45. var (
  46. db *sql.DB
  47. historyDB *sql.DB
  48. assetContentDB *sql.DB
  49. )
  50. func init() {
  51. regex := func(re, s string) (bool, error) {
  52. re = strings.ReplaceAll(re, "\\\\", "\\")
  53. return regexp.MatchString(re, s)
  54. }
  55. sql.Register("sqlite3_extended", &sqlite3.SQLiteDriver{
  56. ConnectHook: func(conn *sqlite3.SQLiteConn) error {
  57. return conn.RegisterFunc("regexp", regex, true)
  58. },
  59. })
  60. }
  61. var initDatabaseLock = sync.Mutex{}
  62. func InitDatabase(forceRebuild bool) (err error) {
  63. initDatabaseLock.Lock()
  64. defer initDatabaseLock.Unlock()
  65. ClearCache()
  66. disableCache()
  67. defer enableCache()
  68. util.IncBootProgress(2, "Initializing database...")
  69. if forceRebuild {
  70. ClearQueue()
  71. }
  72. initDBConnection()
  73. treenode.InitBlockTree(forceRebuild)
  74. if !forceRebuild {
  75. // 检查数据库结构版本,如果版本不一致的话说明改过表结构,需要重建
  76. if util.DatabaseVer == getDatabaseVer() {
  77. return
  78. }
  79. logging.LogInfof("the database structure is changed, rebuilding database...")
  80. }
  81. // 不存在库或者版本不一致都会走到这里
  82. closeDatabase()
  83. if gulu.File.IsExist(util.DBPath) {
  84. if err = removeDatabaseFile(); err != nil {
  85. logging.LogErrorf("remove database file [%s] failed: %s", util.DBPath, err)
  86. util.PushClearProgress()
  87. err = nil
  88. }
  89. }
  90. initDBConnection()
  91. initDBTables()
  92. logging.LogInfof("reinitialized database [%s]", util.DBPath)
  93. return
  94. }
  95. func initDBTables() {
  96. _, err := db.Exec("DROP TABLE IF EXISTS stat")
  97. if err != nil {
  98. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "drop table [stat] failed: %s", err)
  99. }
  100. _, err = db.Exec("CREATE TABLE stat (key, value)")
  101. if err != nil {
  102. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create table [stat] failed: %s", err)
  103. }
  104. setDatabaseVer()
  105. _, err = db.Exec("DROP TABLE IF EXISTS blocks")
  106. if err != nil {
  107. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "drop table [blocks] failed: %s", err)
  108. }
  109. _, err = db.Exec("CREATE TABLE blocks (id, parent_id, root_id, hash, box, path, hpath, name, alias, memo, tag, content, fcontent, markdown, length, type, subtype, ial, sort, created, updated)")
  110. if err != nil {
  111. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create table [blocks] failed: %s", err)
  112. }
  113. _, err = db.Exec("CREATE INDEX idx_blocks_id ON blocks(id)")
  114. if err != nil {
  115. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create index [idx_blocks_id] failed: %s", err)
  116. }
  117. _, err = db.Exec("CREATE INDEX idx_blocks_parent_id ON blocks(parent_id)")
  118. if err != nil {
  119. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create index [idx_blocks_parent_id] failed: %s", err)
  120. }
  121. _, err = db.Exec("CREATE INDEX idx_blocks_root_id ON blocks(root_id)")
  122. if err != nil {
  123. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create index [idx_blocks_root_id] failed: %s", err)
  124. }
  125. _, err = db.Exec("DROP TABLE IF EXISTS blocks_fts")
  126. if err != nil {
  127. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "drop table [blocks_fts] failed: %s", err)
  128. }
  129. _, err = db.Exec("CREATE VIRTUAL TABLE blocks_fts USING fts5(id UNINDEXED, parent_id UNINDEXED, root_id UNINDEXED, hash UNINDEXED, box UNINDEXED, path UNINDEXED, hpath, name, alias, memo, tag, content, fcontent, markdown UNINDEXED, length UNINDEXED, type UNINDEXED, subtype UNINDEXED, ial, sort UNINDEXED, created UNINDEXED, updated UNINDEXED, tokenize=\"siyuan\")")
  130. if err != nil {
  131. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create table [blocks_fts] failed: %s", err)
  132. }
  133. _, err = db.Exec("DROP TABLE IF EXISTS blocks_fts_case_insensitive")
  134. if err != nil {
  135. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "drop table [blocks_fts_case_insensitive] failed: %s", err)
  136. }
  137. _, err = db.Exec("CREATE VIRTUAL TABLE blocks_fts_case_insensitive USING fts5(id UNINDEXED, parent_id UNINDEXED, root_id UNINDEXED, hash UNINDEXED, box UNINDEXED, path UNINDEXED, hpath, name, alias, memo, tag, content, fcontent, markdown UNINDEXED, length UNINDEXED, type UNINDEXED, subtype UNINDEXED, ial, sort UNINDEXED, created UNINDEXED, updated UNINDEXED, tokenize=\"siyuan case_insensitive\")")
  138. if err != nil {
  139. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create table [blocks_fts_case_insensitive] failed: %s", err)
  140. }
  141. _, err = db.Exec("DROP TABLE IF EXISTS spans")
  142. if err != nil {
  143. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "drop table [spans] failed: %s", err)
  144. }
  145. _, err = db.Exec("CREATE TABLE spans (id, block_id, root_id, box, path, content, markdown, type, ial)")
  146. if err != nil {
  147. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create table [spans] failed: %s", err)
  148. }
  149. _, err = db.Exec("CREATE INDEX idx_spans_root_id ON spans(root_id)")
  150. if err != nil {
  151. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create index [idx_spans_root_id] failed: %s", err)
  152. }
  153. _, err = db.Exec("DROP TABLE IF EXISTS assets")
  154. if err != nil {
  155. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "drop table [assets] failed: %s", err)
  156. }
  157. _, err = db.Exec("CREATE TABLE assets (id, block_id, root_id, box, docpath, path, name, title, hash)")
  158. if err != nil {
  159. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create table [assets] failed: %s", err)
  160. }
  161. _, err = db.Exec("CREATE INDEX idx_assets_root_id ON assets(root_id)")
  162. if err != nil {
  163. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create index [idx_assets_root_id] failed: %s", err)
  164. }
  165. _, err = db.Exec("DROP TABLE IF EXISTS attributes")
  166. if err != nil {
  167. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "drop table [attributes] failed: %s", err)
  168. }
  169. _, err = db.Exec("CREATE TABLE attributes (id, name, value, type, block_id, root_id, box, path)")
  170. if err != nil {
  171. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create table [attributes] failed: %s", err)
  172. }
  173. _, err = db.Exec("CREATE INDEX idx_attributes_root_id ON attributes(root_id)")
  174. if err != nil {
  175. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create index [idx_attributes_root_id] failed: %s", err)
  176. }
  177. _, err = db.Exec("DROP TABLE IF EXISTS refs")
  178. if err != nil {
  179. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "drop table [refs] failed: %s", err)
  180. }
  181. _, err = db.Exec("CREATE TABLE refs (id, def_block_id, def_block_parent_id, def_block_root_id, def_block_path, block_id, root_id, box, path, content, markdown, type)")
  182. if err != nil {
  183. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create table [refs] failed: %s", err)
  184. }
  185. _, err = db.Exec("DROP TABLE IF EXISTS file_annotation_refs")
  186. if err != nil {
  187. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "drop table [refs] failed: %s", err)
  188. }
  189. _, err = db.Exec("CREATE TABLE file_annotation_refs (id, file_path, annotation_id, block_id, root_id, box, path, content, type)")
  190. if err != nil {
  191. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create table [refs] failed: %s", err)
  192. }
  193. }
  194. func initDBConnection() {
  195. if nil != db {
  196. closeDatabase()
  197. }
  198. dsn := util.DBPath + "?_journal_mode=WAL" +
  199. "&_synchronous=OFF" +
  200. "&_mmap_size=2684354560" +
  201. "&_secure_delete=OFF" +
  202. "&_cache_size=-20480" +
  203. "&_page_size=32768" +
  204. "&_busy_timeout=7000" +
  205. "&_ignore_check_constraints=ON" +
  206. "&_temp_store=MEMORY" +
  207. "&_case_sensitive_like=OFF"
  208. var err error
  209. db, err = sql.Open("sqlite3_extended", dsn)
  210. if err != nil {
  211. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create database failed: %s", err)
  212. }
  213. db.SetMaxIdleConns(20)
  214. db.SetMaxOpenConns(20)
  215. db.SetConnMaxLifetime(365 * 24 * time.Hour)
  216. }
  217. var initHistoryDatabaseLock = sync.Mutex{}
  218. func InitHistoryDatabase(forceRebuild bool) {
  219. initHistoryDatabaseLock.Lock()
  220. defer initHistoryDatabaseLock.Unlock()
  221. initHistoryDBConnection()
  222. if !forceRebuild && gulu.File.IsExist(util.HistoryDBPath) {
  223. return
  224. }
  225. historyDB.Close()
  226. if err := os.RemoveAll(util.HistoryDBPath); err != nil {
  227. logging.LogErrorf("remove history database file [%s] failed: %s", util.HistoryDBPath, err)
  228. return
  229. }
  230. initHistoryDBConnection()
  231. initHistoryDBTables()
  232. }
  233. func initHistoryDBConnection() {
  234. if nil != historyDB {
  235. historyDB.Close()
  236. }
  237. dsn := util.HistoryDBPath + "?_journal_mode=WAL" +
  238. "&_synchronous=OFF" +
  239. "&_mmap_size=2684354560" +
  240. "&_secure_delete=OFF" +
  241. "&_cache_size=-20480" +
  242. "&_page_size=32768" +
  243. "&_busy_timeout=7000" +
  244. "&_ignore_check_constraints=ON" +
  245. "&_temp_store=MEMORY" +
  246. "&_case_sensitive_like=OFF"
  247. var err error
  248. historyDB, err = sql.Open("sqlite3_extended", dsn)
  249. if err != nil {
  250. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create history database failed: %s", err)
  251. }
  252. historyDB.SetMaxIdleConns(3)
  253. historyDB.SetMaxOpenConns(3)
  254. historyDB.SetConnMaxLifetime(365 * 24 * time.Hour)
  255. }
  256. func initHistoryDBTables() {
  257. historyDB.Exec("DROP TABLE histories_fts_case_insensitive")
  258. _, err := historyDB.Exec("CREATE VIRTUAL TABLE histories_fts_case_insensitive USING fts5(id UNINDEXED, type UNINDEXED, op UNINDEXED, title, content, path UNINDEXED, created UNINDEXED, tokenize=\"siyuan case_insensitive\")")
  259. if err != nil {
  260. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create table [histories_fts_case_insensitive] failed: %s", err)
  261. }
  262. }
  263. var initAssetContentDatabaseLock = sync.Mutex{}
  264. func InitAssetContentDatabase(forceRebuild bool) {
  265. initAssetContentDatabaseLock.Lock()
  266. defer initAssetContentDatabaseLock.Unlock()
  267. initAssetContentDBConnection()
  268. if !forceRebuild && gulu.File.IsExist(util.AssetContentDBPath) {
  269. return
  270. }
  271. assetContentDB.Close()
  272. if err := os.RemoveAll(util.AssetContentDBPath); err != nil {
  273. logging.LogErrorf("remove assets database file [%s] failed: %s", util.AssetContentDBPath, err)
  274. return
  275. }
  276. initAssetContentDBConnection()
  277. initAssetContentDBTables()
  278. }
  279. func initAssetContentDBConnection() {
  280. if nil != assetContentDB {
  281. assetContentDB.Close()
  282. }
  283. dsn := util.AssetContentDBPath + "?_journal_mode=WAL" +
  284. "&_synchronous=OFF" +
  285. "&_mmap_size=2684354560" +
  286. "&_secure_delete=OFF" +
  287. "&_cache_size=-20480" +
  288. "&_page_size=32768" +
  289. "&_busy_timeout=7000" +
  290. "&_ignore_check_constraints=ON" +
  291. "&_temp_store=MEMORY" +
  292. "&_case_sensitive_like=OFF"
  293. var err error
  294. assetContentDB, err = sql.Open("sqlite3_extended", dsn)
  295. if err != nil {
  296. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create assets database failed: %s", err)
  297. }
  298. assetContentDB.SetMaxIdleConns(3)
  299. assetContentDB.SetMaxOpenConns(3)
  300. assetContentDB.SetConnMaxLifetime(365 * 24 * time.Hour)
  301. }
  302. func initAssetContentDBTables() {
  303. assetContentDB.Exec("DROP TABLE asset_contents_fts_case_insensitive")
  304. _, err := assetContentDB.Exec("CREATE VIRTUAL TABLE asset_contents_fts_case_insensitive USING fts5(id UNINDEXED, name, ext, path, size UNINDEXED, updated UNINDEXED, content, tokenize=\"siyuan case_insensitive\")")
  305. if err != nil {
  306. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "create table [asset_contents_fts_case_insensitive] failed: %s", err)
  307. }
  308. }
  309. var (
  310. caseSensitive bool
  311. indexAssetPath bool
  312. )
  313. func SetCaseSensitive(b bool) {
  314. caseSensitive = b
  315. if b {
  316. db.Exec("PRAGMA case_sensitive_like = ON;")
  317. } else {
  318. db.Exec("PRAGMA case_sensitive_like = OFF;")
  319. }
  320. }
  321. func SetIndexAssetPath(b bool) {
  322. indexAssetPath = b
  323. }
  324. func refsFromTree(tree *parse.Tree) (refs []*Ref, fileAnnotationRefs []*FileAnnotationRef) {
  325. ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
  326. if entering {
  327. return ast.WalkContinue
  328. }
  329. if treenode.IsBlockRef(n) {
  330. ref := buildRef(tree, n)
  331. if !isRepeatedRef(refs, ref) {
  332. refs = append(refs, ref)
  333. }
  334. } else if treenode.IsFileAnnotationRef(n) {
  335. pathID := n.TextMarkFileAnnotationRefID
  336. idx := strings.LastIndex(pathID, "/")
  337. if -1 == idx {
  338. return ast.WalkContinue
  339. }
  340. filePath := pathID[:idx]
  341. annotationID := pathID[idx+1:]
  342. anchor := n.TextMarkTextContent
  343. text := filePath
  344. if "" != anchor {
  345. text = anchor
  346. }
  347. parentBlock := treenode.ParentBlock(n)
  348. ref := &FileAnnotationRef{
  349. ID: ast.NewNodeID(),
  350. FilePath: filePath,
  351. AnnotationID: annotationID,
  352. BlockID: parentBlock.ID,
  353. RootID: tree.ID,
  354. Box: tree.Box,
  355. Path: tree.Path,
  356. Content: text,
  357. Type: treenode.TypeAbbr(n.Type.String()),
  358. }
  359. fileAnnotationRefs = append(fileAnnotationRefs, ref)
  360. } else if treenode.IsEmbedBlockRef(n) {
  361. ref := buildEmbedRef(tree, n)
  362. if !isRepeatedRef(refs, ref) {
  363. refs = append(refs, ref)
  364. }
  365. }
  366. return ast.WalkContinue
  367. })
  368. return
  369. }
  370. func isRepeatedRef(refs []*Ref, ref *Ref) bool {
  371. // Repeated references to the same block within a block only count as one reference https://github.com/siyuan-note/siyuan/issues/9670
  372. for _, r := range refs {
  373. if r.DefBlockID == ref.DefBlockID && r.BlockID == ref.BlockID {
  374. return true
  375. }
  376. }
  377. return false
  378. }
  379. func buildRef(tree *parse.Tree, refNode *ast.Node) *Ref {
  380. // 多个类型可能会导致渲染的 Markdown 不正确,所以这里只保留 block-ref 类型
  381. tmpTyp := refNode.TextMarkType
  382. refNode.TextMarkType = "block-ref"
  383. markdown := treenode.ExportNodeStdMd(refNode, luteEngine)
  384. refNode.TextMarkType = tmpTyp
  385. defBlockID, text, _ := treenode.GetBlockRef(refNode)
  386. var defBlockParentID, defBlockRootID, defBlockPath string
  387. defBlock := treenode.GetBlockTree(defBlockID)
  388. if nil != defBlock {
  389. defBlockParentID = defBlock.ParentID
  390. defBlockRootID = defBlock.RootID
  391. defBlockPath = defBlock.Path
  392. }
  393. parentBlock := treenode.ParentBlock(refNode)
  394. return &Ref{
  395. ID: ast.NewNodeID(),
  396. DefBlockID: defBlockID,
  397. DefBlockParentID: defBlockParentID,
  398. DefBlockRootID: defBlockRootID,
  399. DefBlockPath: defBlockPath,
  400. BlockID: parentBlock.ID,
  401. RootID: tree.ID,
  402. Box: tree.Box,
  403. Path: tree.Path,
  404. Content: text,
  405. Markdown: markdown,
  406. Type: treenode.TypeAbbr(refNode.Type.String()),
  407. }
  408. }
  409. func buildEmbedRef(tree *parse.Tree, embedNode *ast.Node) *Ref {
  410. defBlockID := getEmbedRef(embedNode)
  411. var defBlockParentID, defBlockRootID, defBlockPath string
  412. defBlock := treenode.GetBlockTree(defBlockID)
  413. if nil != defBlock {
  414. defBlockParentID = defBlock.ParentID
  415. defBlockRootID = defBlock.RootID
  416. defBlockPath = defBlock.Path
  417. }
  418. return &Ref{
  419. ID: ast.NewNodeID(),
  420. DefBlockID: defBlockID,
  421. DefBlockParentID: defBlockParentID,
  422. DefBlockRootID: defBlockRootID,
  423. DefBlockPath: defBlockPath,
  424. BlockID: embedNode.ID,
  425. RootID: tree.ID,
  426. Box: tree.Box,
  427. Path: tree.Path,
  428. Content: "", // 通过嵌入块构建引用时定义块可能还没有入库,所以这里统一不填充内容
  429. Markdown: "",
  430. Type: treenode.TypeAbbr(embedNode.Type.String()),
  431. }
  432. }
  433. func getEmbedRef(embedNode *ast.Node) (queryBlockID string) {
  434. queryBlockID = treenode.GetEmbedBlockRef(embedNode)
  435. return
  436. }
  437. func fromTree(node *ast.Node, tree *parse.Tree) (blocks []*Block, spans []*Span, assets []*Asset, attributes []*Attribute) {
  438. rootID := tree.Root.ID
  439. boxID := tree.Box
  440. p := tree.Path
  441. ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus {
  442. if !entering {
  443. return ast.WalkContinue
  444. }
  445. // 构造行级元素
  446. spanBlocks, spanSpans, spanAssets, spanAttrs, walkStatus := buildSpanFromNode(n, tree, rootID, boxID, p)
  447. if 0 < len(spanBlocks) {
  448. blocks = append(blocks, spanBlocks...)
  449. }
  450. if 0 < len(spanSpans) {
  451. spans = append(spans, spanSpans...)
  452. }
  453. if 0 < len(spanAssets) {
  454. assets = append(assets, spanAssets...)
  455. }
  456. if 0 < len(spanAttrs) {
  457. attributes = append(attributes, spanAttrs...)
  458. }
  459. // 构造属性
  460. attrs := buildAttributeFromNode(n, rootID, boxID, p)
  461. if 0 < len(attrs) {
  462. attributes = append(attributes, attrs...)
  463. }
  464. if -1 != walkStatus {
  465. return walkStatus
  466. }
  467. // 构造块级元素
  468. if "" == n.ID || !n.IsBlock() {
  469. return ast.WalkContinue
  470. }
  471. b, attrs := buildBlockFromNode(n, tree)
  472. blocks = append(blocks, b)
  473. if 0 < len(attrs) {
  474. attributes = append(attributes, attrs...)
  475. }
  476. return ast.WalkContinue
  477. })
  478. return
  479. }
  480. func buildAttributeFromNode(n *ast.Node, rootID, boxID, p string) (attributes []*Attribute) {
  481. switch n.Type {
  482. case ast.NodeKramdownSpanIAL:
  483. parentBlock := treenode.ParentBlock(n)
  484. attrs := parse.IALValMap(n)
  485. for name, val := range attrs {
  486. if !isAttr(name) {
  487. continue
  488. }
  489. attr := &Attribute{
  490. ID: ast.NewNodeID(),
  491. Name: name,
  492. Value: val,
  493. Type: "s",
  494. BlockID: parentBlock.ID,
  495. RootID: rootID,
  496. Box: boxID,
  497. Path: p,
  498. }
  499. attributes = append(attributes, attr)
  500. }
  501. case ast.NodeKramdownBlockIAL:
  502. attrs := parse.IALValMap(n)
  503. for name, val := range attrs {
  504. if !isAttr(name) {
  505. continue
  506. }
  507. attr := &Attribute{
  508. ID: ast.NewNodeID(),
  509. Name: name,
  510. Value: val,
  511. Type: "b",
  512. BlockID: n.ID,
  513. RootID: rootID,
  514. Box: boxID,
  515. Path: p,
  516. }
  517. attributes = append(attributes, attr)
  518. }
  519. }
  520. return
  521. }
  522. func isAttr(name string) bool {
  523. return strings.HasPrefix(name, "custom-") || "name" == name || "alias" == name || "memo" == name || "bookmark" == name || "fold" == name || "heading-fold" == name || "style" == name
  524. }
  525. func buildSpanFromNode(n *ast.Node, tree *parse.Tree, rootID, boxID, p string) (blocks []*Block, spans []*Span, assets []*Asset, attributes []*Attribute, walkStatus ast.WalkStatus) {
  526. boxLocalPath := filepath.Join(util.DataDir, boxID)
  527. docDirLocalPath := filepath.Join(boxLocalPath, p)
  528. switch n.Type {
  529. case ast.NodeImage:
  530. text := n.Text()
  531. markdown := treenode.ExportNodeStdMd(n, luteEngine)
  532. parentBlock := treenode.ParentBlock(n)
  533. span := &Span{
  534. ID: ast.NewNodeID(),
  535. BlockID: parentBlock.ID,
  536. RootID: rootID,
  537. Box: boxID,
  538. Path: p,
  539. Content: text,
  540. Markdown: markdown,
  541. Type: treenode.TypeAbbr(n.Type.String()),
  542. IAL: treenode.IALStr(n),
  543. }
  544. spans = append(spans, span)
  545. walkStatus = ast.WalkSkipChildren
  546. destNode := n.ChildByType(ast.NodeLinkDest)
  547. if nil == destNode {
  548. return
  549. }
  550. // assetsLinkDestsInTree
  551. if !util.IsAssetLinkDest(destNode.Tokens) {
  552. return
  553. }
  554. dest := gulu.Str.FromBytes(destNode.Tokens)
  555. var title string
  556. if titleNode := n.ChildByType(ast.NodeLinkTitle); nil != titleNode {
  557. title = gulu.Str.FromBytes(titleNode.Tokens)
  558. }
  559. var hash string
  560. var hashErr error
  561. if lp := assetLocalPath(dest, boxLocalPath, docDirLocalPath); "" != lp {
  562. if !gulu.File.IsDir(lp) {
  563. hash, hashErr = util.GetEtag(lp)
  564. if nil != hashErr {
  565. logging.LogErrorf("calc asset [%s] hash failed: %s", lp, hashErr)
  566. }
  567. }
  568. }
  569. name, _ := util.LastID(dest)
  570. asset := &Asset{
  571. ID: ast.NewNodeID(),
  572. BlockID: parentBlock.ID,
  573. RootID: rootID,
  574. Box: boxID,
  575. DocPath: p,
  576. Path: dest,
  577. Name: name,
  578. Title: title,
  579. Hash: hash,
  580. }
  581. assets = append(assets, asset)
  582. return
  583. case ast.NodeTextMark:
  584. typ := treenode.TypeAbbr(n.Type.String()) + " " + n.TextMarkType
  585. text := n.Content()
  586. markdown := treenode.ExportNodeStdMd(n, luteEngine)
  587. parentBlock := treenode.ParentBlock(n)
  588. span := &Span{
  589. ID: ast.NewNodeID(),
  590. BlockID: parentBlock.ID,
  591. RootID: rootID,
  592. Box: boxID,
  593. Path: p,
  594. Content: text,
  595. Markdown: markdown,
  596. Type: typ,
  597. IAL: treenode.IALStr(n),
  598. }
  599. spans = append(spans, span)
  600. if n.IsTextMarkType("a") {
  601. dest := n.TextMarkAHref
  602. if util.IsAssetLinkDest([]byte(dest)) {
  603. var title string
  604. if titleNode := n.ChildByType(ast.NodeLinkTitle); nil != titleNode {
  605. title = gulu.Str.FromBytes(titleNode.Tokens)
  606. }
  607. var hash string
  608. var hashErr error
  609. if lp := assetLocalPath(dest, boxLocalPath, docDirLocalPath); "" != lp {
  610. if !gulu.File.IsDir(lp) {
  611. hash, hashErr = util.GetEtag(lp)
  612. if nil != hashErr {
  613. logging.LogErrorf("calc asset [%s] hash failed: %s", lp, hashErr)
  614. }
  615. }
  616. }
  617. name, _ := util.LastID(dest)
  618. asset := &Asset{
  619. ID: ast.NewNodeID(),
  620. BlockID: parentBlock.ID,
  621. RootID: rootID,
  622. Box: boxID,
  623. DocPath: p,
  624. Path: dest,
  625. Name: name,
  626. Title: title,
  627. Hash: hash,
  628. }
  629. assets = append(assets, asset)
  630. }
  631. }
  632. walkStatus = ast.WalkSkipChildren
  633. return
  634. case ast.NodeDocument:
  635. if asset := docTitleImgAsset(n, boxLocalPath, docDirLocalPath); nil != asset {
  636. assets = append(assets, asset)
  637. }
  638. if tags := docTagSpans(n); 0 < len(tags) {
  639. spans = append(spans, tags...)
  640. }
  641. case ast.NodeInlineHTML, ast.NodeHTMLBlock, ast.NodeIFrame, ast.NodeWidget, ast.NodeAudio, ast.NodeVideo:
  642. nodes, err := html.ParseFragment(bytes.NewReader(n.Tokens), &html.Node{Type: html.ElementNode})
  643. if err != nil {
  644. logging.LogErrorf("parse HTML failed: %s", err)
  645. walkStatus = ast.WalkContinue
  646. return
  647. }
  648. if 1 > len(nodes) &&
  649. ast.NodeHTMLBlock != n.Type { // HTML 块若内容为空时无法在数据库中查询到 https://github.com/siyuan-note/siyuan/issues/4691
  650. walkStatus = ast.WalkContinue
  651. return
  652. }
  653. if ast.NodeHTMLBlock == n.Type || ast.NodeIFrame == n.Type || ast.NodeWidget == n.Type || ast.NodeAudio == n.Type || ast.NodeVideo == n.Type {
  654. b, attrs := buildBlockFromNode(n, tree)
  655. blocks = append(blocks, b)
  656. attributes = append(attributes, attrs...)
  657. }
  658. if ast.NodeInlineHTML == n.Type {
  659. // 没有行级 HTML,只有块级 HTML,这里转换为块
  660. b, attrs := buildBlockFromNode(n, tree)
  661. b.Type = ast.NodeHTMLBlock.String()
  662. blocks = append(blocks, b)
  663. attributes = append(attributes, attrs...)
  664. walkStatus = ast.WalkContinue
  665. return
  666. }
  667. if 1 > len(nodes) {
  668. walkStatus = ast.WalkContinue
  669. return
  670. }
  671. var src []byte
  672. for _, attr := range nodes[0].Attr {
  673. if "src" == attr.Key || strings.HasPrefix(attr.Key, "data-assets") || strings.HasPrefix(attr.Key, "custom-data-assets") {
  674. src = gulu.Str.ToBytes(attr.Val)
  675. break
  676. }
  677. }
  678. if 1 > len(src) {
  679. walkStatus = ast.WalkContinue
  680. return
  681. }
  682. if !util.IsAssetLinkDest(src) {
  683. walkStatus = ast.WalkContinue
  684. return
  685. }
  686. dest := string(src)
  687. var hash string
  688. var hashErr error
  689. if lp := assetLocalPath(dest, boxLocalPath, docDirLocalPath); "" != lp {
  690. hash, hashErr = util.GetEtag(lp)
  691. if nil != hashErr {
  692. logging.LogErrorf("calc asset [%s] hash failed: %s", lp, hashErr)
  693. }
  694. }
  695. parentBlock := treenode.ParentBlock(n)
  696. if ast.NodeInlineHTML != n.Type {
  697. parentBlock = n
  698. }
  699. name, _ := util.LastID(dest)
  700. asset := &Asset{
  701. ID: ast.NewNodeID(),
  702. BlockID: parentBlock.ID,
  703. RootID: rootID,
  704. Box: boxID,
  705. DocPath: p,
  706. Path: dest,
  707. Name: name,
  708. Title: "",
  709. Hash: hash,
  710. }
  711. assets = append(assets, asset)
  712. walkStatus = ast.WalkSkipChildren
  713. return
  714. }
  715. walkStatus = -1
  716. return
  717. }
  718. func BuildBlockFromNode(n *ast.Node, tree *parse.Tree) (block *Block) {
  719. block, _ = buildBlockFromNode(n, tree)
  720. return
  721. }
  722. func buildBlockFromNode(n *ast.Node, tree *parse.Tree) (block *Block, attributes []*Attribute) {
  723. boxID := tree.Box
  724. p := tree.Path
  725. rootID := tree.Root.ID
  726. name := html.UnescapeString(n.IALAttr("name"))
  727. alias := html.UnescapeString(n.IALAttr("alias"))
  728. memo := html.UnescapeString(n.IALAttr("memo"))
  729. tag := tagFromNode(n)
  730. var content, fcontent, markdown, parentID string
  731. ialContent := treenode.IALStr(n)
  732. hash := treenode.NodeHash(n, tree, luteEngine)
  733. var length int
  734. if ast.NodeDocument == n.Type {
  735. content = n.IALAttr("title")
  736. fcontent = content
  737. length = utf8.RuneCountInString(fcontent)
  738. } else if n.IsContainerBlock() {
  739. markdown = treenode.ExportNodeStdMd(n, luteEngine)
  740. if !treenode.IsNodeOCRed(n) {
  741. util.PushNodeOCRQueue(n)
  742. }
  743. content = NodeStaticContent(n, nil, true, indexAssetPath, true)
  744. fc := treenode.FirstLeafBlock(n)
  745. if !treenode.IsNodeOCRed(fc) {
  746. util.PushNodeOCRQueue(fc)
  747. }
  748. fcontent = NodeStaticContent(fc, nil, true, false, true)
  749. parentID = n.Parent.ID
  750. if h := heading(n); nil != h { // 如果在标题块下方,则将标题块作为父节点
  751. parentID = h.ID
  752. }
  753. length = utf8.RuneCountInString(fcontent)
  754. } else {
  755. markdown = treenode.ExportNodeStdMd(n, luteEngine)
  756. if !treenode.IsNodeOCRed(n) {
  757. util.PushNodeOCRQueue(n)
  758. }
  759. content = NodeStaticContent(n, nil, true, indexAssetPath, true)
  760. parentID = n.Parent.ID
  761. if h := heading(n); nil != h {
  762. parentID = h.ID
  763. }
  764. length = utf8.RuneCountInString(content)
  765. }
  766. block = &Block{
  767. ID: n.ID,
  768. ParentID: parentID,
  769. RootID: rootID,
  770. Hash: hash,
  771. Box: boxID,
  772. Path: p,
  773. HPath: tree.HPath,
  774. Name: name,
  775. Alias: alias,
  776. Memo: memo,
  777. Tag: tag,
  778. Content: content,
  779. FContent: fcontent,
  780. Markdown: markdown,
  781. Length: length,
  782. Type: treenode.TypeAbbr(n.Type.String()),
  783. SubType: treenode.SubTypeAbbr(n),
  784. IAL: ialContent,
  785. Sort: nSort(n),
  786. Created: util.TimeFromID(n.ID),
  787. Updated: n.IALAttr("updated"),
  788. }
  789. attrs := parse.IAL2Map(n.KramdownIAL)
  790. for attrName, attrVal := range attrs {
  791. if !isAttr(attrName) {
  792. continue
  793. }
  794. attr := &Attribute{
  795. ID: ast.NewNodeID(),
  796. Name: attrName,
  797. Value: attrVal,
  798. Type: "b",
  799. BlockID: n.ID,
  800. RootID: rootID,
  801. Box: boxID,
  802. Path: p,
  803. }
  804. attributes = append(attributes, attr)
  805. }
  806. return
  807. }
  808. func tagFromNode(node *ast.Node) (ret string) {
  809. tagBuilder := bytes.Buffer{}
  810. if ast.NodeDocument == node.Type {
  811. tagIAL := html.UnescapeString(node.IALAttr("tags"))
  812. tags := strings.Split(tagIAL, ",")
  813. for _, t := range tags {
  814. t = strings.TrimSpace(t)
  815. if "" == t {
  816. continue
  817. }
  818. tagBuilder.WriteString("#")
  819. tagBuilder.WriteString(t)
  820. tagBuilder.WriteString("# ")
  821. }
  822. return strings.TrimSpace(tagBuilder.String())
  823. }
  824. ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus {
  825. if !entering {
  826. return ast.WalkContinue
  827. }
  828. if n.IsTextMarkType("tag") {
  829. tagBuilder.WriteString("#")
  830. tagBuilder.WriteString(n.Text())
  831. tagBuilder.WriteString("# ")
  832. }
  833. return ast.WalkContinue
  834. })
  835. return strings.TrimSpace(tagBuilder.String())
  836. }
  837. func heading(node *ast.Node) *ast.Node {
  838. if nil == node {
  839. return nil
  840. }
  841. currentLevel := 16
  842. if ast.NodeHeading == node.Type {
  843. currentLevel = node.HeadingLevel
  844. }
  845. for prev := node.Previous; nil != prev; prev = prev.Previous {
  846. if ast.NodeHeading == prev.Type {
  847. if prev.HeadingLevel < currentLevel {
  848. return prev
  849. }
  850. }
  851. }
  852. return nil
  853. }
  854. func deleteByBoxTx(tx *sql.Tx, box string) (err error) {
  855. if err = deleteBlocksByBoxTx(tx, box); err != nil {
  856. return
  857. }
  858. if err = deleteSpansByBoxTx(tx, box); err != nil {
  859. return
  860. }
  861. if err = deleteAssetsByBoxTx(tx, box); err != nil {
  862. return
  863. }
  864. if err = deleteAttributesByBoxTx(tx, box); err != nil {
  865. return
  866. }
  867. if err = deleteBlockRefsByBoxTx(tx, box); err != nil {
  868. return
  869. }
  870. if err = deleteFileAnnotationRefsByBoxTx(tx, box); err != nil {
  871. return
  872. }
  873. return
  874. }
  875. func deleteBlocksByIDs(tx *sql.Tx, ids []string) (err error) {
  876. if 1 > len(ids) {
  877. return
  878. }
  879. var ftsIDs []string
  880. for _, id := range ids {
  881. removeBlockCache(id)
  882. ftsIDs = append(ftsIDs, "\""+id+"\"")
  883. }
  884. var rowIDs []string
  885. stmt := "SELECT ROWID FROM blocks WHERE id IN (" + strings.Join(ftsIDs, ",") + ")"
  886. rows, err := tx.Query(stmt)
  887. if err != nil {
  888. logging.LogErrorf("query block rowIDs failed: %s", err)
  889. return
  890. }
  891. for rows.Next() {
  892. var rowID int64
  893. if err = rows.Scan(&rowID); err != nil {
  894. logging.LogErrorf("scan block rowID failed: %s", err)
  895. rows.Close()
  896. return
  897. }
  898. rowIDs = append(rowIDs, strconv.FormatInt(rowID, 10))
  899. }
  900. rows.Close()
  901. if 1 > len(rowIDs) {
  902. return
  903. }
  904. stmt = "DELETE FROM blocks WHERE ROWID IN (" + strings.Join(rowIDs, ",") + ")"
  905. if err = execStmtTx(tx, stmt); err != nil {
  906. return
  907. }
  908. stmt = "DELETE FROM blocks_fts WHERE ROWID IN (" + strings.Join(rowIDs, ",") + ")"
  909. if err = execStmtTx(tx, stmt); err != nil {
  910. return
  911. }
  912. if !caseSensitive {
  913. stmt = "DELETE FROM blocks_fts_case_insensitive WHERE ROWID IN (" + strings.Join(rowIDs, ",") + ")"
  914. if err = execStmtTx(tx, stmt); err != nil {
  915. return
  916. }
  917. }
  918. return
  919. }
  920. func deleteBlocksByBoxTx(tx *sql.Tx, box string) (err error) {
  921. stmt := "DELETE FROM blocks WHERE box = ?"
  922. if err = execStmtTx(tx, stmt, box); err != nil {
  923. return
  924. }
  925. stmt = "DELETE FROM blocks_fts WHERE box = ?"
  926. if err = execStmtTx(tx, stmt, box); err != nil {
  927. return
  928. }
  929. if !caseSensitive {
  930. stmt = "DELETE FROM blocks_fts_case_insensitive WHERE box = ?"
  931. if err = execStmtTx(tx, stmt, box); err != nil {
  932. return
  933. }
  934. }
  935. ClearCache()
  936. return
  937. }
  938. func deleteSpansByRootID(tx *sql.Tx, rootID string) (err error) {
  939. stmt := "DELETE FROM spans WHERE root_id =?"
  940. err = execStmtTx(tx, stmt, rootID)
  941. return
  942. }
  943. func deleteSpansByBoxTx(tx *sql.Tx, box string) (err error) {
  944. stmt := "DELETE FROM spans WHERE box = ?"
  945. err = execStmtTx(tx, stmt, box)
  946. return
  947. }
  948. func deleteAssetsByRootID(tx *sql.Tx, rootID string) (err error) {
  949. stmt := "DELETE FROM assets WHERE root_id = ?"
  950. err = execStmtTx(tx, stmt, rootID)
  951. return
  952. }
  953. func deleteAssetsByBoxTx(tx *sql.Tx, box string) (err error) {
  954. stmt := "DELETE FROM assets WHERE box = ?"
  955. err = execStmtTx(tx, stmt, box)
  956. return
  957. }
  958. func deleteAttributesByRootID(tx *sql.Tx, rootID string) (err error) {
  959. stmt := "DELETE FROM attributes WHERE root_id = ?"
  960. err = execStmtTx(tx, stmt, rootID)
  961. return
  962. }
  963. func deleteAttributesByBoxTx(tx *sql.Tx, box string) (err error) {
  964. stmt := "DELETE FROM attributes WHERE box = ?"
  965. err = execStmtTx(tx, stmt, box)
  966. return
  967. }
  968. func deleteRefsByPath(tx *sql.Tx, box, path string) (err error) {
  969. stmt := "DELETE FROM refs WHERE box = ? AND path = ?"
  970. err = execStmtTx(tx, stmt, box, path)
  971. return
  972. }
  973. func deleteRefsByPathTx(tx *sql.Tx, box, path string) (err error) {
  974. stmt := "DELETE FROM refs WHERE box = ? AND path = ?"
  975. err = execStmtTx(tx, stmt, box, path)
  976. return
  977. }
  978. func deleteRefsByBoxTx(tx *sql.Tx, box string) (err error) {
  979. if err = deleteFileAnnotationRefsByBoxTx(tx, box); err != nil {
  980. return
  981. }
  982. return deleteBlockRefsByBoxTx(tx, box)
  983. }
  984. func deleteBlockRefsByBoxTx(tx *sql.Tx, box string) (err error) {
  985. stmt := "DELETE FROM refs WHERE box = ?"
  986. err = execStmtTx(tx, stmt, box)
  987. return
  988. }
  989. func deleteFileAnnotationRefsByPath(tx *sql.Tx, box, path string) (err error) {
  990. stmt := "DELETE FROM file_annotation_refs WHERE box = ? AND path = ?"
  991. err = execStmtTx(tx, stmt, box, path)
  992. return
  993. }
  994. func deleteFileAnnotationRefsByPathTx(tx *sql.Tx, box, path string) (err error) {
  995. stmt := "DELETE FROM file_annotation_refs WHERE box = ? AND path = ?"
  996. err = execStmtTx(tx, stmt, box, path)
  997. return
  998. }
  999. func deleteFileAnnotationRefsByBoxTx(tx *sql.Tx, box string) (err error) {
  1000. stmt := "DELETE FROM file_annotation_refs WHERE box = ?"
  1001. err = execStmtTx(tx, stmt, box)
  1002. return
  1003. }
  1004. func deleteByRootID(tx *sql.Tx, rootID string, context map[string]interface{}) (err error) {
  1005. stmt := "DELETE FROM blocks WHERE root_id = ?"
  1006. if err = execStmtTx(tx, stmt, rootID); err != nil {
  1007. return
  1008. }
  1009. stmt = "DELETE FROM blocks_fts WHERE root_id = ?"
  1010. if err = execStmtTx(tx, stmt, rootID); err != nil {
  1011. return
  1012. }
  1013. if !caseSensitive {
  1014. stmt = "DELETE FROM blocks_fts_case_insensitive WHERE root_id = ?"
  1015. if err = execStmtTx(tx, stmt, rootID); err != nil {
  1016. return
  1017. }
  1018. }
  1019. stmt = "DELETE FROM spans WHERE root_id = ?"
  1020. if err = execStmtTx(tx, stmt, rootID); err != nil {
  1021. return
  1022. }
  1023. stmt = "DELETE FROM assets WHERE root_id = ?"
  1024. if err = execStmtTx(tx, stmt, rootID); err != nil {
  1025. return
  1026. }
  1027. stmt = "DELETE FROM refs WHERE root_id = ?"
  1028. if err = execStmtTx(tx, stmt, rootID); err != nil {
  1029. return
  1030. }
  1031. stmt = "DELETE FROM file_annotation_refs WHERE root_id = ?"
  1032. if err = execStmtTx(tx, stmt, rootID); err != nil {
  1033. return
  1034. }
  1035. stmt = "DELETE FROM attributes WHERE root_id = ?"
  1036. if err = execStmtTx(tx, stmt, rootID); err != nil {
  1037. return
  1038. }
  1039. ClearCache()
  1040. eventbus.Publish(eventbus.EvtSQLDeleteBlocks, context, rootID)
  1041. return
  1042. }
  1043. func batchDeleteByRootIDs(tx *sql.Tx, rootIDs []string, context map[string]interface{}) (err error) {
  1044. if 1 > len(rootIDs) {
  1045. return
  1046. }
  1047. ids := strings.Join(rootIDs, "','")
  1048. ids = "('" + ids + "')"
  1049. stmt := "DELETE FROM blocks WHERE root_id IN " + ids
  1050. if err = execStmtTx(tx, stmt); err != nil {
  1051. return
  1052. }
  1053. stmt = "DELETE FROM blocks_fts WHERE root_id IN " + ids
  1054. if err = execStmtTx(tx, stmt); err != nil {
  1055. return
  1056. }
  1057. if !caseSensitive {
  1058. stmt = "DELETE FROM blocks_fts_case_insensitive WHERE root_id IN " + ids
  1059. if err = execStmtTx(tx, stmt); err != nil {
  1060. return
  1061. }
  1062. }
  1063. stmt = "DELETE FROM spans WHERE root_id IN " + ids
  1064. if err = execStmtTx(tx, stmt); err != nil {
  1065. return
  1066. }
  1067. stmt = "DELETE FROM assets WHERE root_id IN " + ids
  1068. if err = execStmtTx(tx, stmt); err != nil {
  1069. return
  1070. }
  1071. stmt = "DELETE FROM refs WHERE root_id IN " + ids
  1072. if err = execStmtTx(tx, stmt); err != nil {
  1073. return
  1074. }
  1075. stmt = "DELETE FROM file_annotation_refs WHERE root_id IN " + ids
  1076. if err = execStmtTx(tx, stmt); err != nil {
  1077. return
  1078. }
  1079. stmt = "DELETE FROM attributes WHERE root_id IN " + ids
  1080. if err = execStmtTx(tx, stmt); err != nil {
  1081. return
  1082. }
  1083. ClearCache()
  1084. eventbus.Publish(eventbus.EvtSQLDeleteBlocks, context, fmt.Sprintf("%d", len(rootIDs)))
  1085. return
  1086. }
  1087. func batchDeleteByPathPrefix(tx *sql.Tx, boxID, pathPrefix string) (err error) {
  1088. stmt := "DELETE FROM blocks WHERE box = ? AND path LIKE ?"
  1089. if err = execStmtTx(tx, stmt, boxID, pathPrefix+"%"); err != nil {
  1090. return
  1091. }
  1092. stmt = "DELETE FROM blocks_fts WHERE box = ? AND path LIKE ?"
  1093. if err = execStmtTx(tx, stmt, boxID, pathPrefix+"%"); err != nil {
  1094. return
  1095. }
  1096. if !caseSensitive {
  1097. stmt = "DELETE FROM blocks_fts_case_insensitive WHERE box = ? AND path LIKE ?"
  1098. if err = execStmtTx(tx, stmt, boxID, pathPrefix+"%"); err != nil {
  1099. return
  1100. }
  1101. }
  1102. stmt = "DELETE FROM spans WHERE box = ? AND path LIKE ?"
  1103. if err = execStmtTx(tx, stmt, boxID, pathPrefix+"%"); err != nil {
  1104. return
  1105. }
  1106. stmt = "DELETE FROM assets WHERE box = ? AND docpath LIKE ?"
  1107. if err = execStmtTx(tx, stmt, boxID, pathPrefix+"%"); err != nil {
  1108. return
  1109. }
  1110. stmt = "DELETE FROM refs WHERE box = ? AND path LIKE ?"
  1111. if err = execStmtTx(tx, stmt, boxID, pathPrefix+"%"); err != nil {
  1112. return
  1113. }
  1114. stmt = "DELETE FROM file_annotation_refs WHERE box = ? AND path LIKE ?"
  1115. if err = execStmtTx(tx, stmt, boxID, pathPrefix+"%"); err != nil {
  1116. return
  1117. }
  1118. stmt = "DELETE FROM attributes WHERE box = ? AND path LIKE ?"
  1119. if err = execStmtTx(tx, stmt, boxID, pathPrefix+"%"); err != nil {
  1120. return
  1121. }
  1122. ClearCache()
  1123. return
  1124. }
  1125. func batchUpdatePath(tx *sql.Tx, tree *parse.Tree, context map[string]interface{}) (err error) {
  1126. stmt := "UPDATE blocks SET box = ?, path = ?, hpath = ? WHERE root_id = ?"
  1127. if err = execStmtTx(tx, stmt, tree.Box, tree.Path, tree.HPath, tree.ID); err != nil {
  1128. return
  1129. }
  1130. stmt = "UPDATE blocks_fts SET box = ?, path = ?, hpath = ? WHERE root_id = ?"
  1131. if err = execStmtTx(tx, stmt, tree.Box, tree.Path, tree.HPath, tree.ID); err != nil {
  1132. return
  1133. }
  1134. if !caseSensitive {
  1135. stmt = "UPDATE blocks_fts_case_insensitive SET box = ?, path = ?, hpath = ? WHERE root_id = ?"
  1136. if err = execStmtTx(tx, stmt, tree.Box, tree.Path, tree.HPath, tree.ID); err != nil {
  1137. return
  1138. }
  1139. }
  1140. ClearCache()
  1141. evtHash := fmt.Sprintf("%x", sha256.Sum256([]byte(tree.ID)))[:7]
  1142. eventbus.Publish(eventbus.EvtSQLUpdateBlocksHPaths, context, 1, evtHash)
  1143. return
  1144. }
  1145. func batchUpdateHPath(tx *sql.Tx, tree *parse.Tree, context map[string]interface{}) (err error) {
  1146. stmt := "UPDATE blocks SET hpath = ? WHERE root_id = ?"
  1147. if err = execStmtTx(tx, stmt, tree.HPath, tree.ID); err != nil {
  1148. return
  1149. }
  1150. stmt = "UPDATE blocks_fts SET hpath = ? WHERE root_id = ?"
  1151. if err = execStmtTx(tx, stmt, tree.HPath, tree.ID); err != nil {
  1152. return
  1153. }
  1154. if !caseSensitive {
  1155. stmt = "UPDATE blocks_fts_case_insensitive SET hpath = ? WHERE root_id = ?"
  1156. if err = execStmtTx(tx, stmt, tree.HPath, tree.ID); err != nil {
  1157. return
  1158. }
  1159. }
  1160. ClearCache()
  1161. evtHash := fmt.Sprintf("%x", sha256.Sum256([]byte(tree.ID)))[:7]
  1162. eventbus.Publish(eventbus.EvtSQLUpdateBlocksHPaths, context, 1, evtHash)
  1163. return
  1164. }
  1165. func CloseDatabase() {
  1166. if err := closeDatabase(); err != nil {
  1167. logging.LogErrorf("close database failed: %s", err)
  1168. return
  1169. }
  1170. if err := historyDB.Close(); err != nil {
  1171. logging.LogErrorf("close history database failed: %s", err)
  1172. return
  1173. }
  1174. if err := assetContentDB.Close(); err != nil {
  1175. logging.LogErrorf("close asset content database failed: %s", err)
  1176. return
  1177. }
  1178. treenode.CloseDatabase()
  1179. logging.LogInfof("closed database")
  1180. }
  1181. func queryRow(query string, args ...interface{}) *sql.Row {
  1182. query = strings.TrimSpace(query)
  1183. if "" == query {
  1184. logging.LogErrorf("statement is empty")
  1185. return nil
  1186. }
  1187. if nil == db {
  1188. return nil
  1189. }
  1190. return db.QueryRow(query, args...)
  1191. }
  1192. func query(query string, args ...interface{}) (*sql.Rows, error) {
  1193. query = strings.TrimSpace(query)
  1194. if "" == query {
  1195. return nil, errors.New("statement is empty")
  1196. }
  1197. if nil == db {
  1198. return nil, errors.New("database is nil")
  1199. }
  1200. return db.Query(query, args...)
  1201. }
  1202. func beginTx() (tx *sql.Tx, err error) {
  1203. if tx, err = db.Begin(); err != nil {
  1204. logging.LogErrorf("begin tx failed: %s\n %s", err, logging.ShortStack())
  1205. if strings.Contains(err.Error(), "database is locked") {
  1206. os.Exit(logging.ExitCodeReadOnlyDatabase)
  1207. }
  1208. }
  1209. return
  1210. }
  1211. func commitTx(tx *sql.Tx) (err error) {
  1212. if nil == tx {
  1213. logging.LogErrorf("tx is nil")
  1214. return errors.New("tx is nil")
  1215. }
  1216. if err = tx.Commit(); err != nil {
  1217. logging.LogErrorf("commit tx failed: %s\n %s", err, logging.ShortStack())
  1218. }
  1219. return
  1220. }
  1221. func beginHistoryTx() (tx *sql.Tx, err error) {
  1222. if tx, err = historyDB.Begin(); err != nil {
  1223. logging.LogErrorf("begin history tx failed: %s\n %s", err, logging.ShortStack())
  1224. if strings.Contains(err.Error(), "database is locked") {
  1225. os.Exit(logging.ExitCodeReadOnlyDatabase)
  1226. }
  1227. }
  1228. return
  1229. }
  1230. func commitHistoryTx(tx *sql.Tx) (err error) {
  1231. if nil == tx {
  1232. logging.LogErrorf("tx is nil")
  1233. return errors.New("tx is nil")
  1234. }
  1235. if err = tx.Commit(); err != nil {
  1236. logging.LogErrorf("commit tx failed: %s\n %s", err, logging.ShortStack())
  1237. }
  1238. return
  1239. }
  1240. func beginAssetContentTx() (tx *sql.Tx, err error) {
  1241. if tx, err = assetContentDB.Begin(); err != nil {
  1242. logging.LogErrorf("begin asset content tx failed: %s\n %s", err, logging.ShortStack())
  1243. if strings.Contains(err.Error(), "database is locked") {
  1244. os.Exit(logging.ExitCodeReadOnlyDatabase)
  1245. }
  1246. }
  1247. return
  1248. }
  1249. func commitAssetContentTx(tx *sql.Tx) (err error) {
  1250. if nil == tx {
  1251. logging.LogErrorf("tx is nil")
  1252. return errors.New("tx is nil")
  1253. }
  1254. if err = tx.Commit(); err != nil {
  1255. logging.LogErrorf("commit tx failed: %s\n %s", err, logging.ShortStack())
  1256. }
  1257. return
  1258. }
  1259. func prepareExecInsertTx(tx *sql.Tx, stmtSQL string, args []interface{}) (err error) {
  1260. stmt, err := tx.Prepare(stmtSQL)
  1261. if err != nil {
  1262. return
  1263. }
  1264. if _, err = stmt.Exec(args...); err != nil {
  1265. logging.LogErrorf("exec database stmt [%s] failed: %s", stmtSQL, err)
  1266. return
  1267. }
  1268. return
  1269. }
  1270. func execStmtTx(tx *sql.Tx, stmt string, args ...interface{}) (err error) {
  1271. if _, err = tx.Exec(stmt, args...); err != nil {
  1272. if strings.Contains(err.Error(), "database disk image is malformed") {
  1273. tx.Rollback()
  1274. closeDatabase()
  1275. removeDatabaseFile()
  1276. logging.LogFatalf(logging.ExitCodeReadOnlyDatabase, "database disk image [%s] is malformed, please restart SiYuan kernel to rebuild it", util.DBPath)
  1277. }
  1278. logging.LogErrorf("exec database stmt [%s] failed: %s\n %s", stmt, err, logging.ShortStack())
  1279. return
  1280. }
  1281. return
  1282. }
  1283. func nSort(n *ast.Node) int {
  1284. switch n.Type {
  1285. // 以下为块级元素
  1286. case ast.NodeHeading:
  1287. return 5
  1288. case ast.NodeParagraph:
  1289. return 10
  1290. case ast.NodeCodeBlock:
  1291. return 10
  1292. case ast.NodeMathBlock:
  1293. return 10
  1294. case ast.NodeTable:
  1295. return 10
  1296. case ast.NodeHTMLBlock:
  1297. return 10
  1298. case ast.NodeList:
  1299. return 20
  1300. case ast.NodeListItem:
  1301. return 20
  1302. case ast.NodeBlockquote:
  1303. return 20
  1304. case ast.NodeSuperBlock:
  1305. return 30
  1306. case ast.NodeAttributeView:
  1307. return 30
  1308. case ast.NodeDocument:
  1309. return 0
  1310. case ast.NodeText, ast.NodeTextMark:
  1311. if n.IsTextMarkType("tag") {
  1312. return 205
  1313. }
  1314. return 200
  1315. }
  1316. return 100
  1317. }
  1318. func ialAttr(ial, name string) (ret string) {
  1319. idx := strings.Index(ial, name)
  1320. if 0 > idx {
  1321. return ""
  1322. }
  1323. ret = ial[idx+len(name)+2:]
  1324. ret = ret[:strings.Index(ret, "\"")]
  1325. return
  1326. }
  1327. func removeDatabaseFile() (err error) {
  1328. err = os.RemoveAll(util.DBPath)
  1329. if err != nil {
  1330. return
  1331. }
  1332. err = os.RemoveAll(util.DBPath + "-shm")
  1333. if err != nil {
  1334. return
  1335. }
  1336. err = os.RemoveAll(util.DBPath + "-wal")
  1337. if err != nil {
  1338. return
  1339. }
  1340. return
  1341. }
  1342. func closeDatabase() (err error) {
  1343. if nil == db {
  1344. return
  1345. }
  1346. err = db.Close()
  1347. debug.FreeOSMemory()
  1348. runtime.GC() // 没有这句的话文件句柄不会释放,后面就无法删除文件
  1349. return
  1350. }
  1351. func SQLTemplateFuncs(templateFuncMap *template.FuncMap) {
  1352. (*templateFuncMap)["queryBlocks"] = func(stmt string, args ...string) (retBlocks []*Block) {
  1353. for _, arg := range args {
  1354. stmt = strings.Replace(stmt, "?", arg, 1)
  1355. }
  1356. retBlocks = SelectBlocksRawStmt(stmt, 1, 512)
  1357. return
  1358. }
  1359. (*templateFuncMap)["querySpans"] = func(stmt string, args ...string) (retSpans []*Span) {
  1360. for _, arg := range args {
  1361. stmt = strings.Replace(stmt, "?", arg, 1)
  1362. }
  1363. retSpans = SelectSpansRawStmt(stmt, 512)
  1364. return
  1365. }
  1366. }