database.go 40 KB

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