database.go 38 KB

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