database.go 30 KB

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