database.go 40 KB

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