database.go 40 KB

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