2022-05-26 15:18:53 +08:00
// SiYuan - Build Your Eternal Digital Garden
// Copyright (c) 2020-present, b3log.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package sql
import (
"bytes"
"database/sql"
2023-05-04 10:11:29 +08:00
"math"
2022-05-26 15:18:53 +08:00
"sort"
"strconv"
"strings"
"github.com/88250/lute/ast"
"github.com/88250/vitess-sqlparser/sqlparser"
"github.com/emirpasic/gods/sets/hashset"
2022-07-17 12:22:32 +08:00
"github.com/siyuan-note/logging"
2022-05-26 15:18:53 +08:00
"github.com/siyuan-note/siyuan/kernel/treenode"
"github.com/siyuan-note/siyuan/kernel/util"
)
2023-01-19 01:15:54 +08:00
func QueryEmptyContentEmbedBlocks ( ) ( ret [ ] * Block ) {
2023-01-19 01:12:31 +08:00
stmt := "SELECT * FROM blocks WHERE type = 'query_embed' AND content = ''"
rows , err := query ( stmt )
if nil != err {
logging . LogErrorf ( "sql query [%s] failed: %s" , stmt , err )
return
}
defer rows . Close ( )
for rows . Next ( ) {
if block := scanBlockRows ( rows ) ; nil != block {
ret = append ( ret , block )
}
}
return
}
2022-05-26 15:18:53 +08:00
func queryBlockHashes ( rootID string ) ( ret map [ string ] string ) {
stmt := "SELECT id, hash FROM blocks WHERE root_id = ?"
rows , err := query ( stmt , rootID )
if nil != err {
2022-07-19 01:20:51 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , stmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
ret = map [ string ] string { }
for rows . Next ( ) {
var id , hash string
if err = rows . Scan ( & id , & hash ) ; nil != err {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
ret [ id ] = hash
}
return
}
func QueryRootBlockByCondition ( condition string ) ( ret [ ] * Block ) {
sqlStmt := "SELECT *, length(hpath) - length(replace(hpath, '/', '')) AS lv FROM blocks WHERE type = 'd' AND " + condition + " ORDER BY box DESC,lv ASC LIMIT 128"
rows , err := query ( sqlStmt )
if nil != err {
2022-07-19 01:20:51 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
var block Block
var sepCount int
2022-08-28 10:43:56 +08:00
if err = rows . Scan ( & block . ID , & block . ParentID , & block . RootID , & block . Hash , & block . Box , & block . Path , & block . HPath , & block . Name , & block . Alias , & block . Memo , & block . Tag , & block . Content , & block . FContent , & block . Markdown , & block . Length , & block . Type , & block . SubType , & block . IAL , & block . Sort , & block . Created , & block . Updated , & sepCount ) ; nil != err {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s" , err )
2022-05-26 15:18:53 +08:00
return
}
ret = append ( ret , & block )
}
return
}
func ( block * Block ) IsContainerBlock ( ) bool {
switch block . Type {
case "d" , "b" , "l" , "i" , "s" :
return true
}
return false
}
func queryBlockChildrenIDs ( id string ) ( ret [ ] string ) {
ret = append ( ret , id )
childIDs := queryBlockIDByParentID ( id )
for _ , childID := range childIDs {
ret = append ( ret , queryBlockChildrenIDs ( childID ) ... )
}
return
}
func queryBlockIDByParentID ( parentID string ) ( ret [ ] string ) {
sqlStmt := "SELECT id FROM blocks WHERE parent_id = ?"
rows , err := query ( sqlStmt , parentID )
if nil != err {
2022-07-19 01:20:51 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
var id string
rows . Scan ( & id )
ret = append ( ret , id )
}
return
}
func QueryRecentUpdatedBlocks ( ) ( ret [ ] * Block ) {
sqlStmt := "SELECT * FROM blocks WHERE type = 'p' AND length > 1 ORDER BY updated DESC LIMIT 16"
2022-08-31 12:25:55 +08:00
if util . ContainerIOS == util . Container || util . ContainerAndroid == util . Container {
2022-05-26 15:18:53 +08:00
sqlStmt = "SELECT * FROM blocks WHERE type = 'd' ORDER BY updated DESC LIMIT 16"
}
rows , err := query ( sqlStmt )
if nil != err {
2022-07-19 01:20:51 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
if block := scanBlockRows ( rows ) ; nil != block {
ret = append ( ret , block )
}
}
return
}
func QueryBlockByNameOrAlias ( rootID , text string ) ( ret * Block ) {
sqlStmt := "SELECT * FROM blocks WHERE root_id = ? AND (alias LIKE ? OR name = ?)"
row := queryRow ( sqlStmt , rootID , "%" + text + "%" , text )
ret = scanBlockRow ( row )
return
}
func QueryBlockAliases ( rootID string ) ( ret [ ] string ) {
sqlStmt := "SELECT alias FROM blocks WHERE root_id = ? AND alias != ''"
rows , err := query ( sqlStmt , rootID )
if nil != err {
2022-07-19 01:20:51 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
var aliasesRows [ ] string
for rows . Next ( ) {
var name string
rows . Scan ( & name )
aliasesRows = append ( aliasesRows , name )
}
for _ , aliasStr := range aliasesRows {
aliases := strings . Split ( aliasStr , "," )
for _ , alias := range aliases {
var exist bool
for _ , retAlias := range ret {
if retAlias == alias {
exist = true
}
}
if ! exist {
ret = append ( ret , alias )
}
}
}
return
}
func queryNames ( ) ( ret [ ] string ) {
ret = [ ] string { }
sqlStmt := "SELECT name FROM blocks WHERE name != '' LIMIT ?"
rows , err := query ( sqlStmt , 10240 )
if nil != err {
2022-07-19 01:20:51 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
var namesRows [ ] string
for rows . Next ( ) {
var name string
rows . Scan ( & name )
namesRows = append ( namesRows , name )
}
set := hashset . New ( )
for _ , namesStr := range namesRows {
names := strings . Split ( namesStr , "," )
for _ , name := range names {
if "" == strings . TrimSpace ( name ) {
continue
}
set . Add ( name )
}
}
for _ , v := range set . Values ( ) {
ret = append ( ret , v . ( string ) )
}
return
}
func queryAliases ( ) ( ret [ ] string ) {
ret = [ ] string { }
sqlStmt := "SELECT alias FROM blocks WHERE alias != '' LIMIT ?"
rows , err := query ( sqlStmt , 10240 )
if nil != err {
2022-07-19 01:20:51 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
var aliasesRows [ ] string
for rows . Next ( ) {
var alias string
rows . Scan ( & alias )
aliasesRows = append ( aliasesRows , alias )
}
set := hashset . New ( )
for _ , aliasStr := range aliasesRows {
aliases := strings . Split ( aliasStr , "," )
for _ , alias := range aliases {
if "" == strings . TrimSpace ( alias ) {
continue
}
set . Add ( alias )
}
}
for _ , v := range set . Values ( ) {
ret = append ( ret , v . ( string ) )
}
return
}
func queryDocIDsByTitle ( title string , excludeIDs [ ] string ) ( ret [ ] string ) {
ret = [ ] string { }
notIn := "('" + strings . Join ( excludeIDs , "','" ) + "')"
2023-02-15 18:40:16 +08:00
sqlStmt := "SELECT id FROM blocks WHERE type = 'd' AND content LIKE ? AND id NOT IN " + notIn + " LIMIT ?"
if caseSensitive {
sqlStmt = "SELECT id FROM blocks WHERE type = 'd' AND content = ? AND id NOT IN " + notIn + " LIMIT ?"
}
2022-05-26 15:18:53 +08:00
rows , err := query ( sqlStmt , title , 32 )
if nil != err {
2022-07-19 01:20:51 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
set := hashset . New ( )
for rows . Next ( ) {
var id string
rows . Scan ( & id )
set . Add ( id )
}
for _ , v := range set . Values ( ) {
ret = append ( ret , v . ( string ) )
}
return
}
func queryDocTitles ( ) ( ret [ ] string ) {
ret = [ ] string { }
2023-02-17 15:17:25 +08:00
sqlStmt := "SELECT content FROM blocks WHERE type = 'd'"
rows , err := query ( sqlStmt )
2022-05-26 15:18:53 +08:00
if nil != err {
2022-07-19 01:20:51 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
var docNamesRows [ ] string
for rows . Next ( ) {
var name string
rows . Scan ( & name )
docNamesRows = append ( docNamesRows , name )
}
set := hashset . New ( )
for _ , nameStr := range docNamesRows {
names := strings . Split ( nameStr , "," )
for _ , name := range names {
if "" == strings . TrimSpace ( name ) {
continue
}
set . Add ( name )
}
}
for _ , v := range set . Values ( ) {
ret = append ( ret , v . ( string ) )
}
return
}
func QueryBlockNamesByRootID ( rootID string ) ( ret [ ] string ) {
sqlStmt := "SELECT DISTINCT name FROM blocks WHERE root_id = ? AND name != ''"
rows , err := query ( sqlStmt , rootID )
if nil != err {
2022-07-19 01:20:51 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
var name string
rows . Scan ( & name )
ret = append ( ret , name )
}
return
}
func QueryBookmarkBlocksByKeyword ( bookmark string ) ( ret [ ] * Block ) {
sqlStmt := "SELECT * FROM blocks WHERE ial LIKE ?"
rows , err := query ( sqlStmt , "%bookmark=%" )
if nil != err {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
if block := scanBlockRows ( rows ) ; nil != block {
ret = append ( ret , block )
}
}
return
}
func QueryBookmarkBlocks ( ) ( ret [ ] * Block ) {
sqlStmt := "SELECT * FROM blocks WHERE ial LIKE ?"
rows , err := query ( sqlStmt , "%bookmark=%" )
if nil != err {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
if block := scanBlockRows ( rows ) ; nil != block {
ret = append ( ret , block )
}
}
return
}
func QueryBookmarkLabels ( ) ( ret [ ] string ) {
ret = [ ] string { }
sqlStmt := "SELECT * FROM blocks WHERE ial LIKE ?"
rows , err := query ( sqlStmt , "%bookmark=%" )
if nil != err {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
labels := map [ string ] bool { }
for rows . Next ( ) {
if block := scanBlockRows ( rows ) ; nil != block {
if v := ialAttr ( block . IAL , "bookmark" ) ; "" != v {
labels [ v ] = true
}
}
}
for label := range labels {
ret = append ( ret , label )
}
sort . Strings ( ret )
return
}
2023-05-04 10:11:29 +08:00
func QueryNoLimit ( stmt string ) ( ret [ ] map [ string ] interface { } , err error ) {
return queryRawStmt ( stmt , math . MaxInt )
}
func Query ( stmt string , limit int ) ( ret [ ] map [ string ] interface { } , err error ) {
parsedStmt , err := sqlparser . Parse ( stmt )
if nil != err {
return queryRawStmt ( stmt , limit )
}
switch parsedStmt . ( type ) {
case * sqlparser . Select :
slct := parsedStmt . ( * sqlparser . Select )
if nil == slct . Limit {
slct . Limit = & sqlparser . Limit {
Rowcount : & sqlparser . SQLVal {
Type : sqlparser . IntVal ,
Val : [ ] byte ( strconv . Itoa ( limit ) ) ,
} ,
}
} else {
if nil != slct . Limit . Rowcount && 0 < len ( slct . Limit . Rowcount . ( * sqlparser . SQLVal ) . Val ) {
limit , _ = strconv . Atoi ( string ( slct . Limit . Rowcount . ( * sqlparser . SQLVal ) . Val ) )
if 0 >= limit {
limit = 32
}
}
slct . Limit . Rowcount = & sqlparser . SQLVal {
Type : sqlparser . IntVal ,
Val : [ ] byte ( strconv . Itoa ( limit ) ) ,
}
}
stmt = sqlparser . String ( slct )
default :
return
}
2022-05-26 15:18:53 +08:00
ret = [ ] map [ string ] interface { } { }
rows , err := query ( stmt )
if nil != err {
2022-07-17 12:22:32 +08:00
logging . LogWarnf ( "sql query [%s] failed: %s" , stmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
cols , _ := rows . Columns ( )
if nil == cols {
return
}
for rows . Next ( ) {
columns := make ( [ ] interface { } , len ( cols ) )
columnPointers := make ( [ ] interface { } , len ( cols ) )
for i := range columns {
columnPointers [ i ] = & columns [ i ]
}
if err = rows . Scan ( columnPointers ... ) ; nil != err {
return
}
m := make ( map [ string ] interface { } )
for i , colName := range cols {
val := columnPointers [ i ] . ( * interface { } )
m [ colName ] = * val
}
ret = append ( ret , m )
}
return
}
2023-05-04 10:11:29 +08:00
func queryRawStmt ( stmt string , limit int ) ( ret [ ] map [ string ] interface { } , err error ) {
rows , err := query ( stmt )
if nil != err {
if strings . Contains ( err . Error ( ) , "syntax error" ) {
return
}
return
}
defer rows . Close ( )
cols , err := rows . Columns ( )
if nil != err || nil == cols {
return
}
noLimit := ! strings . Contains ( strings . ToLower ( stmt ) , " limit " )
var count , errCount int
for rows . Next ( ) {
columns := make ( [ ] interface { } , len ( cols ) )
columnPointers := make ( [ ] interface { } , len ( cols ) )
for i := range columns {
columnPointers [ i ] = & columns [ i ]
}
if err = rows . Scan ( columnPointers ... ) ; nil != err {
return
}
m := make ( map [ string ] interface { } )
for i , colName := range cols {
val := columnPointers [ i ] . ( * interface { } )
m [ colName ] = * val
}
ret = append ( ret , m )
count ++
if ( noLimit && limit < count ) || 0 < errCount {
break
}
}
return
}
2022-05-26 15:18:53 +08:00
func SelectBlocksRawStmtNoParse ( stmt string , limit int ) ( ret [ ] * Block ) {
return selectBlocksRawStmt ( stmt , limit )
}
2023-04-21 09:49:13 +08:00
func SelectBlocksRawStmt ( stmt string , page , limit int ) ( ret [ ] * Block ) {
2022-05-26 15:18:53 +08:00
parsedStmt , err := sqlparser . Parse ( stmt )
if nil != err {
return selectBlocksRawStmt ( stmt , limit )
}
switch parsedStmt . ( type ) {
case * sqlparser . Select :
slct := parsedStmt . ( * sqlparser . Select )
if nil == slct . Limit {
slct . Limit = & sqlparser . Limit {
Rowcount : & sqlparser . SQLVal {
Type : sqlparser . IntVal ,
Val : [ ] byte ( strconv . Itoa ( limit ) ) ,
} ,
}
2023-04-21 10:46:55 +08:00
slct . Limit . Offset = & sqlparser . SQLVal {
Type : sqlparser . IntVal ,
Val : [ ] byte ( strconv . Itoa ( ( page - 1 ) * limit ) ) ,
}
2023-04-21 09:49:13 +08:00
} else {
2023-04-21 10:46:55 +08:00
if nil != slct . Limit . Rowcount && 0 < len ( slct . Limit . Rowcount . ( * sqlparser . SQLVal ) . Val ) {
limit , _ = strconv . Atoi ( string ( slct . Limit . Rowcount . ( * sqlparser . SQLVal ) . Val ) )
if 0 >= limit {
limit = 32
}
}
2023-04-21 09:49:13 +08:00
slct . Limit . Rowcount = & sqlparser . SQLVal {
Type : sqlparser . IntVal ,
Val : [ ] byte ( strconv . Itoa ( limit ) ) ,
}
slct . Limit . Offset = & sqlparser . SQLVal {
Type : sqlparser . IntVal ,
2023-04-21 10:46:55 +08:00
Val : [ ] byte ( strconv . Itoa ( ( page - 1 ) * limit ) ) ,
2023-04-21 09:49:13 +08:00
}
2022-05-26 15:18:53 +08:00
}
stmt = sqlparser . String ( slct )
default :
return
}
stmt = strings . ReplaceAll ( stmt , "\\'" , "''" )
stmt = strings . ReplaceAll ( stmt , "\\\"" , "\"" )
stmt = strings . ReplaceAll ( stmt , "\\\\*" , "\\*" )
stmt = strings . ReplaceAll ( stmt , "from dual" , "" )
rows , err := query ( stmt )
if nil != err {
if strings . Contains ( err . Error ( ) , "syntax error" ) {
return
}
2022-07-17 12:22:32 +08:00
logging . LogWarnf ( "sql query [%s] failed: %s" , stmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
if block := scanBlockRows ( rows ) ; nil != block {
ret = append ( ret , block )
}
}
return
}
func selectBlocksRawStmt ( stmt string , limit int ) ( ret [ ] * Block ) {
rows , err := query ( stmt )
if nil != err {
if strings . Contains ( err . Error ( ) , "syntax error" ) {
return
}
return
}
defer rows . Close ( )
2023-05-04 10:11:29 +08:00
noLimit := ! strings . Contains ( strings . ToLower ( stmt ) , " limit " )
2023-02-01 09:53:40 +08:00
var count , errCount int
2022-05-26 15:18:53 +08:00
for rows . Next ( ) {
2023-02-01 09:53:40 +08:00
count ++
2022-05-26 15:18:53 +08:00
if block := scanBlockRows ( rows ) ; nil != block {
ret = append ( ret , block )
2023-01-31 17:28:47 +08:00
} else {
2023-02-01 10:12:34 +08:00
logging . LogWarnf ( "raw sql query [%s] failed" , stmt )
2023-02-01 09:53:40 +08:00
errCount ++
}
2023-05-04 10:11:29 +08:00
if ( noLimit && limit < count ) || 0 < errCount {
2023-02-01 09:53:40 +08:00
break
2022-05-26 15:18:53 +08:00
}
}
return
}
func scanBlockRows ( rows * sql . Rows ) ( ret * Block ) {
var block Block
2022-08-28 10:43:56 +08:00
if err := rows . Scan ( & block . ID , & block . ParentID , & block . RootID , & block . Hash , & block . Box , & block . Path , & block . HPath , & block . Name , & block . Alias , & block . Memo , & block . Tag , & block . Content , & block . FContent , & block . Markdown , & block . Length , & block . Type , & block . SubType , & block . IAL , & block . Sort , & block . Created , & block . Updated ) ; nil != err {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s\n%s" , err , logging . ShortStack ( ) )
2022-05-26 15:18:53 +08:00
return
}
ret = & block
2022-11-20 22:56:51 +08:00
putBlockCache ( ret )
2022-05-26 15:18:53 +08:00
return
}
func scanBlockRow ( row * sql . Row ) ( ret * Block ) {
var block Block
2022-08-28 10:43:56 +08:00
if err := row . Scan ( & block . ID , & block . ParentID , & block . RootID , & block . Hash , & block . Box , & block . Path , & block . HPath , & block . Name , & block . Alias , & block . Memo , & block . Tag , & block . Content , & block . FContent , & block . Markdown , & block . Length , & block . Type , & block . SubType , & block . IAL , & block . Sort , & block . Created , & block . Updated ) ; nil != err {
2022-05-26 15:18:53 +08:00
if sql . ErrNoRows != err {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "query scan field failed: %s\n%s" , err , logging . ShortStack ( ) )
2022-05-26 15:18:53 +08:00
}
return
}
ret = & block
2022-11-21 10:20:11 +08:00
putBlockCache ( ret )
2022-05-26 15:18:53 +08:00
return
}
func GetChildBlocks ( parentID , condition string ) ( ret [ ] * Block ) {
blockIDs := queryBlockChildrenIDs ( parentID )
var params [ ] string
for _ , id := range blockIDs {
params = append ( params , "\"" + id + "\"" )
}
ret = [ ] * Block { }
sqlStmt := "SELECT * FROM blocks AS ref WHERE ref.id IN (" + strings . Join ( params , "," ) + ")"
if "" != condition {
sqlStmt += " AND " + condition
}
rows , err := query ( sqlStmt )
if nil != err {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
if block := scanBlockRows ( rows ) ; nil != block {
ret = append ( ret , block )
}
}
return
}
2023-02-14 10:49:03 +08:00
func GetAllChildBlocks ( rootIDs [ ] string , condition string ) ( ret [ ] * Block ) {
2022-05-26 15:18:53 +08:00
ret = [ ] * Block { }
2023-02-14 10:49:03 +08:00
sqlStmt := "SELECT * FROM blocks AS ref WHERE ref.root_id IN ('" + strings . Join ( rootIDs , "','" ) + "')"
2022-05-26 15:18:53 +08:00
if "" != condition {
sqlStmt += " AND " + condition
}
2023-02-14 10:49:03 +08:00
rows , err := query ( sqlStmt )
2022-05-26 15:18:53 +08:00
if nil != err {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
if block := scanBlockRows ( rows ) ; nil != block {
ret = append ( ret , block )
}
}
return
}
func GetBlock ( id string ) ( ret * Block ) {
ret = getBlockCache ( id )
if nil != ret {
return
}
row := queryRow ( "SELECT * FROM blocks WHERE id = ?" , id )
ret = scanBlockRow ( row )
if nil != ret {
putBlockCache ( ret )
}
return
}
2023-01-26 13:47:12 +08:00
func GetRootUpdated ( ) ( ret map [ string ] string , err error ) {
rows , err := query ( "SELECT root_id, updated FROM `blocks` WHERE type = 'd'" )
2023-01-08 22:47:43 +08:00
if nil != err {
logging . LogErrorf ( "sql query failed: %s" , err )
return
}
defer rows . Close ( )
2023-01-09 10:34:37 +08:00
ret = map [ string ] string { }
2023-01-08 22:47:43 +08:00
for rows . Next ( ) {
2023-01-09 10:34:37 +08:00
var rootID , updated string
rows . Scan ( & rootID , & updated )
ret [ rootID ] = updated
2023-01-09 10:25:50 +08:00
}
return
}
2023-01-19 17:36:55 +08:00
func GetDuplicatedRootIDs ( blocksTable string ) ( ret [ ] string ) {
rows , err := query ( "SELECT DISTINCT root_id FROM `" + blocksTable + "` GROUP BY id HAVING COUNT(*) > 1" )
2023-01-09 10:25:50 +08:00
if nil != err {
logging . LogErrorf ( "sql query failed: %s" , err )
return
}
defer rows . Close ( )
for rows . Next ( ) {
var id string
rows . Scan ( & id )
ret = append ( ret , id )
2023-01-08 22:47:43 +08:00
}
return
}
2022-05-26 15:18:53 +08:00
func GetAllRootBlocks ( ) ( ret [ ] * Block ) {
stmt := "SELECT * FROM blocks WHERE type = 'd'"
rows , err := query ( stmt )
if nil != err {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , stmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
if block := scanBlockRows ( rows ) ; nil != block {
ret = append ( ret , block )
}
}
return
}
func GetBlocks ( ids [ ] string ) ( ret [ ] * Block ) {
2022-11-26 22:19:48 +08:00
var notHitIDs [ ] string
cached := map [ string ] * Block { }
for _ , id := range ids {
b := getBlockCache ( id )
if nil != b {
cached [ id ] = b
} else {
notHitIDs = append ( notHitIDs , id )
}
}
if 1 > len ( notHitIDs ) {
for _ , id := range ids {
ret = append ( ret , cached [ id ] )
}
return
}
length := len ( notHitIDs )
2022-05-26 15:18:53 +08:00
stmtBuilder := bytes . Buffer { }
stmtBuilder . WriteString ( "SELECT * FROM blocks WHERE id IN (" )
var args [ ] interface { }
2022-11-26 22:19:48 +08:00
for i , id := range notHitIDs {
2022-05-26 15:18:53 +08:00
args = append ( args , id )
stmtBuilder . WriteByte ( '?' )
if i < length - 1 {
stmtBuilder . WriteByte ( ',' )
}
}
stmtBuilder . WriteString ( ")" )
sqlStmt := stmtBuilder . String ( )
rows , err := query ( sqlStmt , args ... )
if nil != err {
2022-07-17 12:22:32 +08:00
logging . LogErrorf ( "sql query [%s] failed: %s" , sqlStmt , err )
2022-05-26 15:18:53 +08:00
return
}
defer rows . Close ( )
for rows . Next ( ) {
if block := scanBlockRows ( rows ) ; nil != block {
putBlockCache ( block )
2022-11-26 22:19:48 +08:00
cached [ block . ID ] = block
2022-05-26 15:18:53 +08:00
}
}
2022-11-26 22:19:48 +08:00
for _ , id := range ids {
ret = append ( ret , cached [ id ] )
}
2022-05-26 15:18:53 +08:00
return
}
func GetContainerText ( container * ast . Node ) string {
buf := & bytes . Buffer { }
buf . Grow ( 4096 )
leaf := treenode . FirstLeafBlock ( container )
ast . Walk ( leaf , func ( n * ast . Node , entering bool ) ast . WalkStatus {
if ! entering {
return ast . WalkContinue
}
switch n . Type {
2023-01-03 10:49:17 +08:00
case ast . NodeText , ast . NodeLinkText , ast . NodeFileAnnotationRefText , ast . NodeCodeBlockCode , ast . NodeMathBlockContent :
2022-05-26 15:18:53 +08:00
buf . Write ( n . Tokens )
2022-09-14 19:45:51 +08:00
case ast . NodeTextMark :
2022-09-14 19:53:22 +08:00
buf . WriteString ( n . Content ( ) )
2023-01-03 10:49:17 +08:00
case ast . NodeBlockRef :
if anchor := n . ChildByType ( ast . NodeBlockRefText ) ; nil != anchor {
buf . WriteString ( anchor . Text ( ) )
} else if anchor = n . ChildByType ( ast . NodeBlockRefDynamicText ) ; nil != anchor {
buf . WriteString ( anchor . Text ( ) )
} else {
text := GetRefText ( n . TokensStr ( ) )
buf . WriteString ( text )
}
return ast . WalkSkipChildren
2022-05-26 15:18:53 +08:00
}
return ast . WalkContinue
} )
return buf . String ( )
}