database.go 34 KB

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