database.go 32 KB

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