database.go 34 KB

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