database.go 39 KB

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