123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848 |
- // SiYuan - Refactor your thinking
- // 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"
- "math"
- "sort"
- "strconv"
- "strings"
- "github.com/88250/lute/ast"
- "github.com/88250/vitess-sqlparser/sqlparser"
- "github.com/emirpasic/gods/sets/hashset"
- sqlparser2 "github.com/rqlite/sql"
- "github.com/siyuan-note/logging"
- "github.com/siyuan-note/siyuan/kernel/treenode"
- "github.com/siyuan-note/siyuan/kernel/util"
- )
- func QueryEmptyContentEmbedBlocks() (ret []*Block) {
- 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
- }
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", stmt, err)
- return
- }
- defer rows.Close()
- ret = map[string]string{}
- for rows.Next() {
- var id, hash string
- if err = rows.Scan(&id, &hash); nil != err {
- logging.LogErrorf("query scan field failed: %s", err)
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- return
- }
- defer rows.Close()
- for rows.Next() {
- var block Block
- var sepCount int
- 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 {
- logging.LogErrorf("query scan field failed: %s", err)
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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"
- if util.ContainerIOS == util.Container || util.ContainerAndroid == util.Container {
- sqlStmt = "SELECT * FROM blocks WHERE type = 'd' ORDER BY updated DESC LIMIT 16"
- }
- rows, err := query(sqlStmt)
- if nil != err {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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, "','") + "')"
- 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 ?"
- }
- rows, err := query(sqlStmt, title, 32)
- if nil != err {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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{}
- sqlStmt := "SELECT content FROM blocks WHERE type = 'd'"
- rows, err := query(sqlStmt)
- if nil != err {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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
- }
- 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) {
- // Kernel API `/api/query/sql` support `||` operator https://github.com/siyuan-note/siyuan/issues/9662
- // 这里为了支持 || 操作符,使用了另一个 sql 解析器,但是这个解析器无法处理 UNION https://github.com/siyuan-note/siyuan/issues/8226
- // 考虑到 UNION 的使用场景不多,这里还是以支持 || 操作符为主
- p := sqlparser2.NewParser(strings.NewReader(stmt))
- parsedStmt2, err := p.ParseStatement()
- if nil != err {
- if !strings.Contains(stmt, "||") {
- // 这个解析器无法处理 || 连接字符串操作符
- parsedStmt, err2 := sqlparser.Parse(stmt)
- if nil != err2 {
- return queryRawStmt(stmt, limit)
- }
- switch parsedStmt.(type) {
- case *sqlparser.Select:
- limitClause := getLimitClause(parsedStmt, limit)
- slct := parsedStmt.(*sqlparser.Select)
- slct.Limit = limitClause
- stmt = sqlparser.String(slct)
- case *sqlparser.Union:
- // Kernel API `/api/query/sql` support `UNION` statement https://github.com/siyuan-note/siyuan/issues/8226
- limitClause := getLimitClause(parsedStmt, limit)
- union := parsedStmt.(*sqlparser.Union)
- union.Limit = limitClause
- stmt = sqlparser.String(union)
- default:
- return queryRawStmt(stmt, limit)
- }
- } else {
- return queryRawStmt(stmt, limit)
- }
- } else {
- switch parsedStmt2.(type) {
- case *sqlparser2.SelectStatement:
- slct := parsedStmt2.(*sqlparser2.SelectStatement)
- if nil == slct.LimitExpr {
- slct.LimitExpr = &sqlparser2.NumberLit{Value: strconv.Itoa(limit)}
- }
- stmt = slct.String()
- default:
- return queryRawStmt(stmt, limit)
- }
- }
- ret = []map[string]interface{}{}
- rows, err := query(stmt)
- if nil != err {
- logging.LogWarnf("sql query [%s] failed: %s", stmt, err)
- 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
- }
- func getLimitClause(parsedStmt sqlparser.Statement, limit int) (ret *sqlparser.Limit) {
- switch parsedStmt.(type) {
- case *sqlparser.Select:
- slct := parsedStmt.(*sqlparser.Select)
- if nil != slct.Limit {
- ret = slct.Limit
- }
- case *sqlparser.Union:
- union := parsedStmt.(*sqlparser.Union)
- if nil != union.Limit {
- ret = union.Limit
- }
- }
- if nil == ret || nil == ret.Rowcount {
- ret = &sqlparser.Limit{
- Rowcount: &sqlparser.SQLVal{
- Type: sqlparser.IntVal,
- Val: []byte(strconv.Itoa(limit)),
- },
- }
- }
- return
- }
- 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 := !containsLimitClause(stmt)
- 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
- }
- func SelectBlocksRawStmtNoParse(stmt string, limit int) (ret []*Block) {
- return selectBlocksRawStmt(stmt, limit)
- }
- func SelectBlocksRawStmt(stmt string, page, limit int) (ret []*Block) {
- 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)),
- },
- }
- slct.Limit.Offset = &sqlparser.SQLVal{
- Type: sqlparser.IntVal,
- Val: []byte(strconv.Itoa((page - 1) * 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)),
- }
- slct.Limit.Offset = &sqlparser.SQLVal{
- Type: sqlparser.IntVal,
- Val: []byte(strconv.Itoa((page - 1) * limit)),
- }
- }
- 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
- }
- logging.LogWarnf("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
- }
- 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()
- noLimit := !containsLimitClause(stmt)
- var count, errCount int
- for rows.Next() {
- count++
- if block := scanBlockRows(rows); nil != block {
- ret = append(ret, block)
- } else {
- logging.LogWarnf("raw sql query [%s] failed", stmt)
- errCount++
- }
- if (noLimit && limit < count) || 0 < errCount {
- break
- }
- }
- return
- }
- func scanBlockRows(rows *sql.Rows) (ret *Block) {
- var block Block
- 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 {
- logging.LogErrorf("query scan field failed: %s\n%s", err, logging.ShortStack())
- return
- }
- ret = &block
- putBlockCache(ret)
- return
- }
- func scanBlockRow(row *sql.Row) (ret *Block) {
- var block Block
- 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 {
- if sql.ErrNoRows != err {
- logging.LogErrorf("query scan field failed: %s\n%s", err, logging.ShortStack())
- }
- return
- }
- ret = &block
- putBlockCache(ret)
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- return
- }
- defer rows.Close()
- for rows.Next() {
- if block := scanBlockRows(rows); nil != block {
- ret = append(ret, block)
- }
- }
- return
- }
- func GetAllChildBlocks(rootIDs []string, condition string) (ret []*Block) {
- ret = []*Block{}
- sqlStmt := "SELECT * FROM blocks AS ref WHERE ref.root_id IN ('" + strings.Join(rootIDs, "','") + "')"
- if "" != condition {
- sqlStmt += " AND " + condition
- }
- rows, err := query(sqlStmt)
- if nil != err {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- 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
- }
- func GetRootUpdated() (ret map[string]string, err error) {
- rows, err := query("SELECT root_id, updated FROM `blocks` WHERE type = 'd'")
- if nil != err {
- logging.LogErrorf("sql query failed: %s", err)
- return
- }
- defer rows.Close()
- ret = map[string]string{}
- for rows.Next() {
- var rootID, updated string
- rows.Scan(&rootID, &updated)
- ret[rootID] = updated
- }
- return
- }
- func GetDuplicatedRootIDs(blocksTable string) (ret []string) {
- rows, err := query("SELECT DISTINCT root_id FROM `" + blocksTable + "` GROUP BY id HAVING COUNT(*) > 1")
- 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)
- }
- return
- }
- func GetAllRootBlocks() (ret []*Block) {
- stmt := "SELECT * FROM blocks WHERE type = 'd'"
- 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
- }
- func GetBlocks(ids []string) (ret []*Block) {
- 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)
- stmtBuilder := bytes.Buffer{}
- stmtBuilder.WriteString("SELECT * FROM blocks WHERE id IN (")
- var args []interface{}
- for i, id := range notHitIDs {
- 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 {
- logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err)
- return
- }
- defer rows.Close()
- for rows.Next() {
- if block := scanBlockRows(rows); nil != block {
- putBlockCache(block)
- cached[block.ID] = block
- }
- }
- for _, id := range ids {
- ret = append(ret, cached[id])
- }
- return
- }
- func GetContainerText(container *ast.Node) string {
- buf := &bytes.Buffer{}
- buf.Grow(4096)
- leaf := treenode.FirstLeafBlock(container)
- if nil == leaf {
- return ""
- }
- ast.Walk(leaf, func(n *ast.Node, entering bool) ast.WalkStatus {
- if !entering {
- return ast.WalkContinue
- }
- switch n.Type {
- case ast.NodeText, ast.NodeLinkText, ast.NodeFileAnnotationRefText, ast.NodeCodeBlockCode, ast.NodeMathBlockContent:
- buf.Write(n.Tokens)
- case ast.NodeTextMark:
- buf.WriteString(n.Content())
- 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
- }
- return ast.WalkContinue
- })
- return buf.String()
- }
- func containsLimitClause(stmt string) bool {
- return strings.Contains(strings.ToLower(stmt), " limit ") ||
- strings.Contains(strings.ToLower(stmt), "\nlimit ") ||
- strings.Contains(strings.ToLower(stmt), "\tlimit ")
- }
|