database.go 35 KB

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