database.go 30 KB

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