@ -158,8 +158,8 @@ func (db *database) savePost(p *post, o *postCreationOptions) error {
sqlBuilder . WriteString ( "begin;" )
// Delete old post
if ! o . new {
sqlBuilder . WriteString ( "delete from posts where path = ?;" )
sqlArgs = append ( sqlArgs , o . oldPath )
sqlBuilder . WriteString ( "delete from posts where path = ?;delete from post_parameters where path = ?; " )
sqlArgs = append ( sqlArgs , o . oldPath , o . oldPath )
}
// Insert new post
sqlBuilder . WriteString ( "insert into posts (path, content, published, updated, blog, section, status) values (?, ?, ?, ?, ?, ?, ?);" )
@ -204,7 +204,7 @@ func (db *database) deletePost(path string) (*post, error) {
if err != nil {
return nil , err
}
_ , err = db . exec ( "delete from posts where path = @path" , sql . Named ( "path" , p . Path ) )
_ , err = db . exec ( "begin;delete from posts where path = ?;delete from post_parameters where path = ?;commit;" , dbNoCache , p . Path , p . Path )
if err != nil {
return nil , err
}
@ -226,134 +226,134 @@ type postsRequestConfig struct {
parameterValue string
publishedYear , publishedMonth , publishedDay int
randomOrder bool
withoutParameters bool
}
func buildPostsQuery ( c * postsRequestConfig ) ( query string , args [ ] interface { } ) {
func buildPostsQuery ( c * postsRequestConfig , selection string ) ( query string , args [ ] interface { } ) {
args = [ ] interface { } { }
selection := "select p.path as path, coalesce(content, '') as content, coalesce(published, '') as published, coalesce(updated, '') as updated, coalesce(blog, '') as blog, coalesce(section, '') as section, coalesce(status, '') as status, coalesce(parameter, '') as parameter, coalesce(value, '') as value "
table := "posts"
if c . search != "" {
table = "posts_fts(@search)"
args = append ( args , sql . Named ( "search" , c . search ) )
}
var wheres [ ] string
if c . path != "" {
wheres = append ( wheres , "path = @path" )
args = append ( args , sql . Named ( "path" , c . path ) )
}
if c . status != "" && c . status != statusNil {
table = "(select * from " + table + " where status = @status)"
wheres = append ( wheres , "status = @status" )
args = append ( args , sql . Named ( "status" , c . status ) )
}
if c . blog != "" {
table = "(select * from " + table + " where blog = @blog)"
wheres = append ( wheres , "blog = @blog" )
args = append ( args , sql . Named ( "blog" , c . blog ) )
}
if c . parameter != "" {
table = "(select distinct p.* from " + table + " p left outer join post_parameters pp on p.path = pp.path where pp.parameter = @param "
args = append ( args , sql . Named ( "param" , c . parameter ) )
if c . parameterValue != "" {
table += "and pp.value = @paramval)"
args = append ( args , sql . Named ( "paramval" , c . parameterValue ) )
wheres = append ( wheres , "path in (select path from post_parameters where parameter = @param and value = @paramval)" )
args = append ( args , sql . Named ( "param" , c . parameter ) , sql . Named ( "param val" , c . parameterValue ) )
} else {
table += "and length(coalesce(pp.value, '')) > 1)"
wheres = append ( wheres , "path in (select path from post_parameters where parameter = @param and length(coalesce(value, '')) > 0)" )
args = append ( args , sql . Named ( "param" , c . parameter ) )
}
}
if c . taxonomy != nil && len ( c . taxonomyValue ) > 0 {
table = "(select distinct p.* from " + table + " p left outer join post_parameters pp on p.path = pp.path where pp.parameter = @taxname and lower(pp.value) = lower(@taxval))"
wheres = append ( wheres , "path in (select path from post_parameters where parameter = @taxname and lower(value) = lower(@taxval))" )
args = append ( args , sql . Named ( "taxname" , c . taxonomy . Name ) , sql . Named ( "taxval" , c . taxonomyValue ) )
}
if len ( c . sections ) > 0 {
table = "(select * from " + table + " where section in ("
ws := " section in ("
for i , section := range c . sections {
if i > 0 {
table += ", "
ws += ", "
}
named := fmt . Sprintf ( "section%v" , i )
table += "@" + named
ws += "@" + named
args = append ( args , sql . Named ( named , section ) )
}
table += "))"
ws += ")"
wheres = append ( wheres , ws )
}
if c . publishedYear != 0 {
table = "(select * from " + table + " p where substr(p.published, 1, 4) = @publishedyear)"
wheres = append ( wheres , "substr(published, 1, 4) = @publishedyear" )
args = append ( args , sql . Named ( "publishedyear" , fmt . Sprintf ( "%0004d" , c . publishedYear ) ) )
}
if c . publishedMonth != 0 {
table = "(select * from " + table + " p where substr(p.published, 6, 2) = @publishedmonth)"
wheres = append ( wheres , "substr(published, 6, 2) = @publishedmonth" )
args = append ( args , sql . Named ( "publishedmonth" , fmt . Sprintf ( "%02d" , c . publishedMonth ) ) )
}
if c . publishedDay != 0 {
table = "(select * from " + table + " p where substr(p.published, 9, 2) = @publishedday)"
wheres = append ( wheres , "substr(published, 9, 2) = @publishedday" )
args = append ( args , sql . Named ( "publishedday" , fmt . Sprintf ( "%02d" , c . publishedDay ) ) )
}
tables := " from " + table + " p left outer join post_parameters pp on p.path = pp.path "
sorting := " order by p.published desc "
if len ( wheres ) > 0 {
table += " where " + strings . Join ( wheres , " and " )
}
sorting := " order by published desc"
if c . randomOrder {
sorting = " order by random() "
sorting = " order by random()"
}
if c . path != "" {
query = selection + tables + " where p.path = @path" + sorting
args = append ( args , sql . Named ( "path" , c . path ) )
} else if c . limit != 0 || c . offset != 0 {
query = selection + " from (select * from " + table + " p " + sorting + " limit @limit offset @offset) p left outer join post_parameters pp on p.path = pp.path "
table += sorting
if c . limit != 0 || c . offset != 0 {
table += " limit @limit offset @offset"
args = append ( args , sql . Named ( "limit" , c . limit ) , sql . Named ( "offset" , c . offset ) )
} else {
query = selection + tables + sorting
}
return
query = "select " + selection + " from " + table
return query , args
}
func ( d * database ) getPostParameters ( path string ) ( params map [ string ] [ ] string , err error ) {
rows , err := d . query ( "select parameter, value from post_parameters where path = @path order by id" , sql . Named ( "path" , path ) )
if err != nil {
return nil , err
}
var name , value string
params = map [ string ] [ ] string { }
for rows . Next ( ) {
if err = rows . Scan ( & name , & value ) ; err != nil {
return nil , err
}
params [ name ] = append ( params [ name ] , value )
}
return params , nil
}
func ( d * database ) getPosts ( config * postsRequestConfig ) ( posts [ ] * post , err error ) {
// Query posts
query , queryParams := buildPostsQuery ( config )
query , queryParams := buildPostsQuery ( config , "path, coalesce(content, ''), coalesce(published, ''), coalesce(updated, ''), blog, coalesce(section, ''), status" )
rows , err := d . query ( query , queryParams ... )
if err != nil {
return nil , err
}
// Prepare row scanning (this is a bit dirty, but it's much faster)
postsMap := map [ string ] * post { }
var postsOrder [ ] string
var path , parameterName , parameterValue string
columns , _ := rows . Columns ( )
rawBuffer := make ( [ ] sql . RawBytes , len ( columns ) )
scanArgs := make ( [ ] interface { } , len ( columns ) )
for i := range rawBuffer {
scanArgs [ i ] = & rawBuffer [ i ]
}
// Prepare row scanning
var path , content , published , updated , blog , section , status string
for rows . Next ( ) {
if err = rows . Scan ( scanArgs ... ) ; err != nil {
if err = rows . Scan ( & path , & content , & published , & updated , & blog , & section , & status ) ; err != nil {
return nil , err
}
path = string ( rawBuffer [ 0 ] )
parameterName = string ( rawBuffer [ 7 ] )
parameterValue = string ( rawBuffer [ 8 ] )
if p , ok := postsMap [ path ] ; ok {
// Post already exists, add parameter
p . Parameters [ parameterName ] = append ( p . Parameters [ parameterName ] , parameterValue )
} else {
// Create new post, fill and add to map
p := & post {
Path : path ,
Content : string ( rawBuffer [ 1 ] ) ,
Published : toLocalSafe ( string ( rawBuffer [ 2 ] ) ) ,
Updated : toLocalSafe ( string ( rawBuffer [ 3 ] ) ) ,
Blog : string ( rawBuffer [ 4 ] ) ,
Section : string ( rawBuffer [ 5 ] ) ,
Status : postStatus ( string ( rawBuffer [ 6 ] ) ) ,
Parameters : map [ string ] [ ] string { } ,
}
if parameterName != "" {
p . Parameters [ parameterName ] = append ( p . Parameters [ parameterName ] , parameterValue )
// Create new post, fill and add to list
p := & post {
Path : path ,
Content : content ,
Published : toLocalSafe ( published ) ,
Updated : toLocalSafe ( updated ) ,
Blog : blog ,
Section : section ,
Status : postStatus ( status ) ,
}
if ! config . withoutParameters {
if p . Parameters , err = d . getPostParameters ( path ) ; err != nil {
return nil , err
}
postsMap [ path ] = p
postsOrder = append ( postsOrder , path )
}
}
// Copy map items to list, because map has a random order
for _ , path = range postsOrder {
posts = append ( posts , postsMap [ path ] )
posts = append ( posts , p )
}
return posts , nil
}
func ( d * database ) getPost ( path string ) ( * post , error ) {
posts , err := d . getPosts ( & postsRequestConfig { path : path } )
posts , err := d . getPosts ( & postsRequestConfig { path : path , limit : 1 } )
if err != nil {
return nil , err
} else if len ( posts ) == 0 {
@ -368,9 +368,8 @@ func (d *database) getDrafts(blog string) []*post {
}
func ( d * database ) countPosts ( config * postsRequestConfig ) ( count int , err error ) {
query , params := buildPostsQuery ( config )
query = "select count(distinct path) from (" + query + ")"
row , err := d . queryRow ( query , params ... )
query , params := buildPostsQuery ( config , "path" )
row , err := d . queryRow ( "select count(distinct path) from (" + query + ")" , params ... )
if err != nil {
return
}
@ -394,29 +393,36 @@ func (d *database) allPostPaths(status postStatus) ([]string, error) {
return postPaths , nil
}
func ( a * goBlog ) getRandomPostPath ( blog string ) ( string , error ) {
func ( a * goBlog ) getRandomPostPath ( blog string ) ( path string , err error ) {
sections , ok := funk . Keys ( a . cfg . Blogs [ blog ] . Sections ) . ( [ ] string )
if ! ok {
return "" , errors . New ( "no sections" )
}
posts , err := a . db . getPosts ( & postsRequestConfig { randomOrder : true , limit : 1 , blog : blog , sections : sections } )
query , params := buildPostsQuery ( & postsRequestConfig { randomOrder : true , limit : 1 , blog : blog , sections : sections } , "path" )
row , err := a . db . queryRow ( query , params ... )
if err != nil {
return "" , err
} else if len ( posts ) == 0 {
return
}
err = row . Scan ( & path )
if errors . Is ( err , sql . ErrNoRows ) {
return "" , errPostNotFound
} else if err != nil {
return "" , err
}
return posts [ 0 ] . Path , nil
return path , nil
}
func ( d * database ) allTaxonomyValues ( blog string , taxonomy string ) ( [ ] string , error ) {
var values [ ] string
rows , err := d . query ( "select distinct pp.value from posts p left outer join post_parameters pp on p.path = pp.path where pp.parameter = @tax and length(coalesce(pp.value, '')) > 1 and blog = @blog and status = @status " , sql . Named ( "tax" , taxonomy ) , sql . Named ( "blog" , blog ) , sql . Named ( "status" , statusPublished ) )
rows , err := d . query ( "select distinct value from post_parameters where parameter = @tax and length(coalesce(value, '')) > 0 and path in (select path from posts where blog = @blog and status = @status) order by value " , sql . Named ( "tax" , taxonomy ) , sql . Named ( "blog" , blog ) , sql . Named ( "status" , statusPublished ) )
if err != nil {
return nil , err
}
var value string
for rows . Next ( ) {
var value string
_ = rows . Scan ( & value )
if err = rows . Scan ( & value ) ; err != nil {
return nil , err
}
values = append ( values , value )
}
return values , nil