2022-01-19 19:51:08 +00:00
package sqlite
import (
2022-01-20 21:29:58 +00:00
"database/sql"
2022-01-19 19:51:08 +00:00
"embed"
2022-02-03 16:44:08 +00:00
"encoding/json"
"fmt"
2022-01-19 19:51:08 +00:00
"github.com/ChronosX88/yans/internal/config"
"github.com/ChronosX88/yans/internal/models"
2022-01-20 21:29:58 +00:00
"github.com/ChronosX88/yans/internal/utils"
"github.com/dlclark/regexp2"
2022-01-19 19:51:08 +00:00
"github.com/jmoiron/sqlx"
2022-01-20 21:29:58 +00:00
"github.com/mattn/go-sqlite3"
2022-01-19 19:51:08 +00:00
_ "github.com/mattn/go-sqlite3"
"github.com/pressly/goose/v3"
2022-02-03 16:44:08 +00:00
"strings"
2022-01-19 19:51:08 +00:00
)
//go:embed migrations/*.sql
var migrations embed . FS
type SQLiteBackend struct {
db * sqlx . DB
}
2022-01-20 21:29:58 +00:00
func regexHelper ( re , s string ) ( bool , error ) {
return regexp2 . MustCompile ( re , regexp2 . None ) . MatchString ( s )
}
2022-01-19 19:51:08 +00:00
func NewSQLiteBackend ( cfg config . SQLiteBackendConfig ) ( * SQLiteBackend , error ) {
2022-01-20 21:29:58 +00:00
sql . Register ( "sqlite3_with_regexp" ,
& sqlite3 . SQLiteDriver {
ConnectHook : func ( conn * sqlite3 . SQLiteConn ) error {
return conn . RegisterFunc ( "regexp" , regexHelper , true )
} ,
} )
db , err := sqlx . Open ( "sqlite3_with_regexp" , cfg . Path )
2022-01-19 19:51:08 +00:00
if err != nil {
return nil , err
}
goose . SetBaseFS ( migrations )
if err := goose . SetDialect ( "sqlite3" ) ; err != nil {
return nil , err
}
if err := goose . Up ( db . DB , "migrations" ) ; err != nil {
return nil , err
}
return & SQLiteBackend {
db : db ,
} , nil
}
func ( sb * SQLiteBackend ) ListGroups ( ) ( [ ] models . Group , error ) {
var groups [ ] models . Group
return groups , sb . db . Select ( & groups , "SELECT * FROM groups" )
}
2022-01-20 21:29:58 +00:00
func ( sb * SQLiteBackend ) ListGroupsByPattern ( pattern string ) ( [ ] models . Group , error ) {
var groups [ ] models . Group
w , err := utils . ParseWildmat ( pattern )
if err != nil {
return nil , err
}
r , err := w . ToRegex ( )
if err != nil {
return nil , err
}
return groups , sb . db . Select ( & groups , "SELECT * FROM groups WHERE group_name REGEXP ?" , r . String ( ) )
}
2022-02-03 17:39:08 +00:00
func ( sb * SQLiteBackend ) GetArticlesCount ( g * models . Group ) ( int , error ) {
2022-01-19 19:51:08 +00:00
var count int
2022-01-19 21:44:27 +00:00
return count , sb . db . Get ( & count , "SELECT COUNT(*) FROM articles_to_groups WHERE group_id = ?" , g . ID )
}
2022-02-03 17:39:08 +00:00
func ( sb * SQLiteBackend ) GetGroupHighWaterMark ( g * models . Group ) ( int , error ) {
2022-01-19 21:44:27 +00:00
var waterMark int
2022-02-03 18:24:40 +00:00
return waterMark , sb . db . Get ( & waterMark , "SELECT max(article_number) FROM articles_to_groups WHERE group_id = ?" , g . ID )
2022-01-19 21:44:27 +00:00
}
2022-02-03 17:39:08 +00:00
func ( sb * SQLiteBackend ) GetGroupLowWaterMark ( g * models . Group ) ( int , error ) {
2022-01-19 21:44:27 +00:00
var waterMark int
2022-02-03 18:24:40 +00:00
return waterMark , sb . db . Get ( & waterMark , "SELECT min(article_number) FROM articles_to_groups WHERE group_id = ?" , g . ID )
2022-01-19 19:51:08 +00:00
}
2022-01-25 16:27:58 +00:00
func ( sb * SQLiteBackend ) GetGroup ( groupName string ) ( models . Group , error ) {
var group models . Group
return group , sb . db . Get ( & group , "SELECT * FROM groups WHERE group_name = ?" , groupName )
}
2022-01-25 21:29:30 +00:00
func ( sb * SQLiteBackend ) GetNewGroupsSince ( timestamp int64 ) ( [ ] models . Group , error ) {
var groups [ ] models . Group
2022-02-03 16:44:08 +00:00
return groups , sb . db . Select ( & groups , "SELECT * FROM groups WHERE created_at > datetime(?, 'unixepoch')" , timestamp )
}
func ( sb * SQLiteBackend ) SaveArticle ( a models . Article , groups [ ] string ) error {
res , err := sb . db . Exec ( "INSERT INTO articles (header, body, thread) VALUES (?, ?, ?)" , a . HeaderRaw , a . Body , a . Thread )
articleID , err := res . LastInsertId ( )
if err != nil {
return err
}
var groupIDs [ ] int
for _ , v := range groups {
v = strings . TrimSpace ( v )
g , err := sb . GetGroup ( v )
if err != nil {
if err == sql . ErrNoRows {
return fmt . Errorf ( "no such newsgroup" )
} else {
return err
}
}
groupIDs = append ( groupIDs , g . ID )
}
for _ , v := range groupIDs {
2022-02-03 18:24:40 +00:00
_ , err = sb . db . Exec ( "INSERT INTO articles_to_groups (article_id, article_number, group_id) VALUES (?, (SELECT ifnull(max(article_number)+1, 1) FROM articles_to_groups), ?)" , articleID , v )
2022-02-03 16:44:08 +00:00
if err != nil {
return err
}
}
return err
}
func ( sb * SQLiteBackend ) GetArticle ( messageID string ) ( models . Article , error ) {
var a models . Article
2022-02-05 10:51:50 +00:00
if err := sb . db . Get ( & a , "SELECT * FROM articles WHERE json_extract(articles.header, '$.Message-Id[0]') = ?" , messageID ) ; err != nil {
return a , err
}
return a , json . Unmarshal ( [ ] byte ( a . HeaderRaw ) , & a . Header )
}
func ( sb * SQLiteBackend ) GetArticleByNumber ( g * models . Group , num int ) ( models . Article , error ) {
var a models . Article
if err := sb . db . Get ( & a , "SELECT articles.* FROM articles INNER JOIN articles_to_groups atg on atg.article_id = articles.id WHERE atg.article_number = ? AND atg.group_id = ?" , num , g . ID ) ; err != nil {
2022-02-03 16:44:08 +00:00
return a , err
}
return a , json . Unmarshal ( [ ] byte ( a . HeaderRaw ) , & a . Header )
2022-01-25 21:29:30 +00:00
}
2022-02-03 17:39:08 +00:00
func ( sb * SQLiteBackend ) GetArticleNumbers ( g * models . Group , low , high int64 ) ( [ ] int64 , error ) {
var numbers [ ] int64
if high == 0 && low == 0 {
2022-02-03 18:24:40 +00:00
if err := sb . db . Select ( & numbers , "SELECT article_number FROM articles_to_groups WHERE group_id = ?" , g . ID ) ; err != nil {
2022-02-03 17:39:08 +00:00
return nil , err
}
} else if low == - 1 && high != 0 {
2022-02-03 18:24:40 +00:00
if err := sb . db . Select ( & numbers , "SELECT article_number FROM articles_to_groups WHERE group_id = ? AND article_number = ?" , g . ID , high ) ; err != nil {
2022-02-03 17:39:08 +00:00
return nil , err
}
} else if low != 0 && high == - 1 {
2022-02-03 18:24:40 +00:00
if err := sb . db . Select ( & numbers , "SELECT article_number FROM articles_to_groups WHERE group_id = ? AND article_number > ?" , g . ID , low ) ; err != nil {
2022-02-03 17:39:08 +00:00
return nil , err
}
} else if low == - 1 && high == - 1 {
return nil , nil
} else {
2022-02-03 18:24:40 +00:00
if err := sb . db . Select ( & numbers , "SELECT article_number FROM articles_to_groups WHERE group_id = ? AND article_number > ? AND article_number < ?" , g . ID , low , high ) ; err != nil {
2022-02-03 17:39:08 +00:00
return nil , err
}
}
return numbers , nil
}