github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/store/sqlstore/post_store.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package sqlstore 5 6 import ( 7 "database/sql" 8 "fmt" 9 "net/http" 10 "regexp" 11 "strconv" 12 "strings" 13 "sync" 14 15 sq "github.com/Masterminds/squirrel" 16 "github.com/mattermost/mattermost-server/v5/einterfaces" 17 "github.com/mattermost/mattermost-server/v5/mlog" 18 "github.com/mattermost/mattermost-server/v5/model" 19 "github.com/mattermost/mattermost-server/v5/store" 20 "github.com/mattermost/mattermost-server/v5/utils" 21 ) 22 23 type SqlPostStore struct { 24 SqlStore 25 metrics einterfaces.MetricsInterface 26 maxPostSizeOnce sync.Once 27 maxPostSizeCached int 28 } 29 30 func (s *SqlPostStore) ClearCaches() { 31 } 32 33 func postSliceColumns() []string { 34 return []string{"Id", "CreateAt", "UpdateAt", "EditAt", "DeleteAt", "IsPinned", "UserId", "ChannelId", "RootId", "ParentId", "OriginalId", "Message", "Type", "Props", "Hashtags", "Filenames", "FileIds", "HasReactions"} 35 } 36 37 func postToSlice(post *model.Post) []interface{} { 38 return []interface{}{ 39 post.Id, 40 post.CreateAt, 41 post.UpdateAt, 42 post.EditAt, 43 post.DeleteAt, 44 post.IsPinned, 45 post.UserId, 46 post.ChannelId, 47 post.RootId, 48 post.ParentId, 49 post.OriginalId, 50 post.Message, 51 post.Type, 52 model.StringInterfaceToJson(post.Props), 53 post.Hashtags, 54 model.ArrayToJson(post.Filenames), 55 model.ArrayToJson(post.FileIds), 56 post.HasReactions, 57 } 58 } 59 60 func newSqlPostStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.PostStore { 61 s := &SqlPostStore{ 62 SqlStore: sqlStore, 63 metrics: metrics, 64 maxPostSizeCached: model.POST_MESSAGE_MAX_RUNES_V1, 65 } 66 67 for _, db := range sqlStore.GetAllConns() { 68 table := db.AddTableWithName(model.Post{}, "Posts").SetKeys(false, "Id") 69 table.ColMap("Id").SetMaxSize(26) 70 table.ColMap("UserId").SetMaxSize(26) 71 table.ColMap("ChannelId").SetMaxSize(26) 72 table.ColMap("RootId").SetMaxSize(26) 73 table.ColMap("ParentId").SetMaxSize(26) 74 table.ColMap("OriginalId").SetMaxSize(26) 75 table.ColMap("Message").SetMaxSize(model.POST_MESSAGE_MAX_BYTES_V2) 76 table.ColMap("Type").SetMaxSize(26) 77 table.ColMap("Hashtags").SetMaxSize(1000) 78 table.ColMap("Props").SetMaxSize(8000) 79 table.ColMap("Filenames").SetMaxSize(model.POST_FILENAMES_MAX_RUNES) 80 table.ColMap("FileIds").SetMaxSize(150) 81 } 82 83 return s 84 } 85 86 func (s *SqlPostStore) createIndexesIfNotExists() { 87 s.CreateIndexIfNotExists("idx_posts_update_at", "Posts", "UpdateAt") 88 s.CreateIndexIfNotExists("idx_posts_create_at", "Posts", "CreateAt") 89 s.CreateIndexIfNotExists("idx_posts_delete_at", "Posts", "DeleteAt") 90 s.CreateIndexIfNotExists("idx_posts_channel_id", "Posts", "ChannelId") 91 s.CreateIndexIfNotExists("idx_posts_root_id", "Posts", "RootId") 92 s.CreateIndexIfNotExists("idx_posts_user_id", "Posts", "UserId") 93 s.CreateIndexIfNotExists("idx_posts_is_pinned", "Posts", "IsPinned") 94 95 s.CreateCompositeIndexIfNotExists("idx_posts_channel_id_update_at", "Posts", []string{"ChannelId", "UpdateAt"}) 96 s.CreateCompositeIndexIfNotExists("idx_posts_channel_id_delete_at_create_at", "Posts", []string{"ChannelId", "DeleteAt", "CreateAt"}) 97 98 s.CreateFullTextIndexIfNotExists("idx_posts_message_txt", "Posts", "Message") 99 s.CreateFullTextIndexIfNotExists("idx_posts_hashtags_txt", "Posts", "Hashtags") 100 } 101 102 func (s *SqlPostStore) SaveMultiple(posts []*model.Post) ([]*model.Post, int, *model.AppError) { 103 channelNewPosts := make(map[string]int) 104 maxDateNewPosts := make(map[string]int64) 105 rootIds := make(map[string]int) 106 maxDateRootIds := make(map[string]int64) 107 for idx, post := range posts { 108 if len(post.Id) > 0 { 109 return nil, idx, model.NewAppError("SqlPostStore.Save", "store.sql_post.save.existing.app_error", nil, "id="+post.Id, http.StatusBadRequest) 110 } 111 post.PreSave() 112 maxPostSize := s.GetMaxPostSize() 113 if err := post.IsValid(maxPostSize); err != nil { 114 return nil, idx, err 115 } 116 117 currentChannelCount, ok := channelNewPosts[post.ChannelId] 118 if !ok { 119 if post.IsJoinLeaveMessage() { 120 channelNewPosts[post.ChannelId] = 0 121 } else { 122 channelNewPosts[post.ChannelId] = 1 123 } 124 maxDateNewPosts[post.ChannelId] = post.CreateAt 125 } else { 126 if !post.IsJoinLeaveMessage() { 127 channelNewPosts[post.ChannelId] = currentChannelCount + 1 128 } 129 if post.CreateAt > maxDateNewPosts[post.ChannelId] { 130 maxDateNewPosts[post.ChannelId] = post.CreateAt 131 } 132 } 133 134 if len(post.RootId) == 0 { 135 continue 136 } 137 138 currentRootCount, ok := rootIds[post.RootId] 139 if !ok { 140 rootIds[post.RootId] = 1 141 maxDateRootIds[post.RootId] = post.CreateAt 142 } else { 143 rootIds[post.RootId] = currentRootCount + 1 144 if post.CreateAt > maxDateRootIds[post.RootId] { 145 maxDateRootIds[post.RootId] = post.CreateAt 146 } 147 } 148 } 149 150 query := s.getQueryBuilder().Insert("Posts").Columns(postSliceColumns()...) 151 for _, post := range posts { 152 query = query.Values(postToSlice(post)...) 153 } 154 sql, args, err := query.ToSql() 155 if err != nil { 156 return nil, -1, model.NewAppError("SqlPostStore.Save", "store.sql_post.save.app_error", nil, err.Error(), http.StatusInternalServerError) 157 } 158 159 if _, err := s.GetMaster().Exec(sql, args...); err != nil { 160 return nil, -1, model.NewAppError("SqlPostStore.Save", "store.sql_post.save.app_error", nil, err.Error(), http.StatusInternalServerError) 161 } 162 163 for channelId, count := range channelNewPosts { 164 if _, err := s.GetMaster().Exec("UPDATE Channels SET LastPostAt = GREATEST(:LastPostAt, LastPostAt), TotalMsgCount = TotalMsgCount + :Count WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": maxDateNewPosts[channelId], "ChannelId": channelId, "Count": count}); err != nil { 165 mlog.Error("Error updating Channel LastPostAt.", mlog.Err(err)) 166 } 167 } 168 169 for rootId := range rootIds { 170 if _, err := s.GetMaster().Exec("UPDATE Posts SET UpdateAt = :UpdateAt WHERE Id = :RootId", map[string]interface{}{"UpdateAt": maxDateRootIds[rootId], "RootId": rootId}); err != nil { 171 mlog.Error("Error updating Post UpdateAt.", mlog.Err(err)) 172 } 173 } 174 175 unknownRepliesPosts := []*model.Post{} 176 for _, post := range posts { 177 if len(post.RootId) == 0 { 178 count, ok := rootIds[post.Id] 179 if ok { 180 post.ReplyCount += int64(count) 181 } 182 } else { 183 unknownRepliesPosts = append(unknownRepliesPosts, post) 184 } 185 } 186 187 if len(unknownRepliesPosts) > 0 { 188 if err := s.populateReplyCount(unknownRepliesPosts); err != nil { 189 mlog.Error("Unable to populate the reply count in some posts.", mlog.Err(err)) 190 } 191 } 192 193 return posts, -1, nil 194 } 195 196 func (s *SqlPostStore) Save(post *model.Post) (*model.Post, *model.AppError) { 197 posts, _, err := s.SaveMultiple([]*model.Post{post}) 198 if err != nil { 199 return nil, err 200 } 201 return posts[0], nil 202 } 203 204 func (s *SqlPostStore) populateReplyCount(posts []*model.Post) *model.AppError { 205 rootIds := []string{} 206 for _, post := range posts { 207 rootIds = append(rootIds, post.RootId) 208 } 209 countList := []struct { 210 RootId string 211 Count int64 212 }{} 213 query := s.getQueryBuilder().Select("RootId, COUNT(Id) AS Count").From("Posts").Where(sq.Eq{"RootId": rootIds}).Where(sq.Eq{"DeleteAt": 0}).GroupBy("RootId") 214 215 queryString, args, err := query.ToSql() 216 if err != nil { 217 return model.NewAppError("SqlPostStore.populateReplyCount", "store.sql_post.populate_reply_count.app_error", nil, err.Error(), http.StatusInternalServerError) 218 } 219 _, err = s.GetMaster().Select(&countList, queryString, args...) 220 if err != nil { 221 return model.NewAppError("SqlPostStore.populateReplyCount", "store.sql_post.populate_reply_count.app_error", nil, err.Error(), http.StatusInternalServerError) 222 } 223 224 counts := map[string]int64{} 225 for _, count := range countList { 226 counts[count.RootId] = count.Count 227 } 228 229 for _, post := range posts { 230 count, ok := counts[post.RootId] 231 if !ok { 232 post.ReplyCount = 0 233 } 234 post.ReplyCount = count 235 } 236 237 return nil 238 } 239 240 func (s *SqlPostStore) Update(newPost *model.Post, oldPost *model.Post) (*model.Post, *model.AppError) { 241 newPost.UpdateAt = model.GetMillis() 242 newPost.PreCommit() 243 244 oldPost.DeleteAt = newPost.UpdateAt 245 oldPost.UpdateAt = newPost.UpdateAt 246 oldPost.OriginalId = oldPost.Id 247 oldPost.Id = model.NewId() 248 oldPost.PreCommit() 249 250 maxPostSize := s.GetMaxPostSize() 251 252 if err := newPost.IsValid(maxPostSize); err != nil { 253 return nil, err 254 } 255 256 if _, err := s.GetMaster().Update(newPost); err != nil { 257 return nil, model.NewAppError("SqlPostStore.Update", "store.sql_post.update.app_error", nil, "id="+newPost.Id+", "+err.Error(), http.StatusInternalServerError) 258 } 259 260 time := model.GetMillis() 261 s.GetMaster().Exec("UPDATE Channels SET LastPostAt = :LastPostAt WHERE Id = :ChannelId AND LastPostAt < :LastPostAt", map[string]interface{}{"LastPostAt": time, "ChannelId": newPost.ChannelId}) 262 263 if len(newPost.RootId) > 0 { 264 s.GetMaster().Exec("UPDATE Posts SET UpdateAt = :UpdateAt WHERE Id = :RootId AND UpdateAt < :UpdateAt", map[string]interface{}{"UpdateAt": time, "RootId": newPost.RootId}) 265 } 266 267 // mark the old post as deleted 268 s.GetMaster().Insert(oldPost) 269 270 return newPost, nil 271 } 272 273 func (s *SqlPostStore) OverwriteMultiple(posts []*model.Post) ([]*model.Post, int, *model.AppError) { 274 updateAt := model.GetMillis() 275 maxPostSize := s.GetMaxPostSize() 276 for idx, post := range posts { 277 post.UpdateAt = updateAt 278 if appErr := post.IsValid(maxPostSize); appErr != nil { 279 return nil, idx, appErr 280 } 281 } 282 283 tx, err := s.GetMaster().Begin() 284 if err != nil { 285 return nil, -1, model.NewAppError("SqlPostStore.Overwrite", "store.sql_post.overwrite.app_error", nil, err.Error(), http.StatusInternalServerError) 286 } 287 for idx, post := range posts { 288 if _, err = tx.Update(post); err != nil { 289 txErr := tx.Rollback() 290 if txErr != nil { 291 return nil, idx, model.NewAppError("SqlPostStore.Overwrite", "store.sql_post.overwrite.app_error", nil, txErr.Error(), http.StatusInternalServerError) 292 } 293 294 return nil, idx, model.NewAppError("SqlPostStore.Overwrite", "store.sql_post.overwrite.app_error", nil, "id="+post.Id+", "+err.Error(), http.StatusInternalServerError) 295 } 296 } 297 err = tx.Commit() 298 if err != nil { 299 return nil, -1, model.NewAppError("SqlPostStore.Overwrite", "store.sql_post.overwrite.app_error", nil, err.Error(), http.StatusInternalServerError) 300 } 301 302 return posts, -1, nil 303 } 304 305 func (s *SqlPostStore) Overwrite(post *model.Post) (*model.Post, *model.AppError) { 306 posts, _, err := s.OverwriteMultiple([]*model.Post{post}) 307 if err != nil { 308 return nil, err 309 } 310 311 return posts[0], nil 312 } 313 314 func (s *SqlPostStore) GetFlaggedPosts(userId string, offset int, limit int) (*model.PostList, *model.AppError) { 315 pl := model.NewPostList() 316 317 var posts []*model.Post 318 if _, err := s.GetReplica().Select(&posts, "SELECT *, (SELECT count(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount FROM Posts p WHERE Id IN (SELECT Name FROM Preferences WHERE UserId = :UserId AND Category = :Category) AND DeleteAt = 0 ORDER BY CreateAt DESC LIMIT :Limit OFFSET :Offset", map[string]interface{}{"UserId": userId, "Category": model.PREFERENCE_CATEGORY_FLAGGED_POST, "Offset": offset, "Limit": limit}); err != nil { 319 return nil, model.NewAppError("SqlPostStore.GetFlaggedPosts", "store.sql_post.get_flagged_posts.app_error", nil, err.Error(), http.StatusInternalServerError) 320 } 321 322 for _, post := range posts { 323 pl.AddPost(post) 324 pl.AddOrder(post.Id) 325 } 326 327 return pl, nil 328 } 329 330 func (s *SqlPostStore) GetFlaggedPostsForTeam(userId, teamId string, offset int, limit int) (*model.PostList, *model.AppError) { 331 pl := model.NewPostList() 332 333 var posts []*model.Post 334 335 query := ` 336 SELECT 337 A.*, (SELECT count(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN A.RootId = '' THEN A.Id ELSE A.RootId END) AND Posts.DeleteAt = 0) as ReplyCount 338 FROM 339 (SELECT 340 * 341 FROM 342 Posts 343 WHERE 344 Id 345 IN 346 (SELECT 347 Name 348 FROM 349 Preferences 350 WHERE 351 UserId = :UserId 352 AND Category = :Category) 353 AND DeleteAt = 0 354 ) as A 355 INNER JOIN Channels as B 356 ON B.Id = A.ChannelId 357 WHERE B.TeamId = :TeamId OR B.TeamId = '' 358 ORDER BY CreateAt DESC 359 LIMIT :Limit OFFSET :Offset` 360 361 if _, err := s.GetReplica().Select(&posts, query, map[string]interface{}{"UserId": userId, "Category": model.PREFERENCE_CATEGORY_FLAGGED_POST, "Offset": offset, "Limit": limit, "TeamId": teamId}); err != nil { 362 return nil, model.NewAppError("SqlPostStore.GetFlaggedPostsForTeam", "store.sql_post.get_flagged_posts.app_error", nil, err.Error(), http.StatusInternalServerError) 363 } 364 365 for _, post := range posts { 366 pl.AddPost(post) 367 pl.AddOrder(post.Id) 368 } 369 370 return pl, nil 371 } 372 373 func (s *SqlPostStore) GetFlaggedPostsForChannel(userId, channelId string, offset int, limit int) (*model.PostList, *model.AppError) { 374 pl := model.NewPostList() 375 376 var posts []*model.Post 377 query := ` 378 SELECT 379 *, (SELECT count(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount 380 FROM Posts p 381 WHERE 382 Id IN (SELECT Name FROM Preferences WHERE UserId = :UserId AND Category = :Category) 383 AND ChannelId = :ChannelId 384 AND DeleteAt = 0 385 ORDER BY CreateAt DESC 386 LIMIT :Limit OFFSET :Offset` 387 388 if _, err := s.GetReplica().Select(&posts, query, map[string]interface{}{"UserId": userId, "Category": model.PREFERENCE_CATEGORY_FLAGGED_POST, "ChannelId": channelId, "Offset": offset, "Limit": limit}); err != nil { 389 return nil, model.NewAppError("SqlPostStore.GetFlaggedPostsForChannel", "store.sql_post.get_flagged_posts.app_error", nil, err.Error(), http.StatusInternalServerError) 390 } 391 for _, post := range posts { 392 pl.AddPost(post) 393 pl.AddOrder(post.Id) 394 } 395 396 return pl, nil 397 } 398 399 func (s *SqlPostStore) Get(id string, skipFetchThreads bool) (*model.PostList, *model.AppError) { 400 pl := model.NewPostList() 401 402 if len(id) == 0 { 403 return nil, model.NewAppError("SqlPostStore.GetPost", "store.sql_post.get.app_error", nil, "id="+id, http.StatusBadRequest) 404 } 405 406 var post model.Post 407 postFetchQuery := "SELECT p.*, (SELECT count(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount FROM Posts p WHERE p.Id = :Id AND p.DeleteAt = 0" 408 err := s.GetReplica().SelectOne(&post, postFetchQuery, map[string]interface{}{"Id": id}) 409 if err != nil { 410 return nil, model.NewAppError("SqlPostStore.GetPost", "store.sql_post.get.app_error", nil, "id="+id+err.Error(), http.StatusNotFound) 411 } 412 pl.AddPost(&post) 413 pl.AddOrder(id) 414 if !skipFetchThreads { 415 rootId := post.RootId 416 417 if rootId == "" { 418 rootId = post.Id 419 } 420 421 if len(rootId) == 0 { 422 return nil, model.NewAppError("SqlPostStore.GetPost", "store.sql_post.get.app_error", nil, "root_id="+rootId, http.StatusInternalServerError) 423 } 424 425 var posts []*model.Post 426 _, err = s.GetReplica().Select(&posts, "SELECT *, (SELECT count(Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount FROM Posts p WHERE (Id = :Id OR RootId = :RootId) AND DeleteAt = 0", map[string]interface{}{"Id": rootId, "RootId": rootId}) 427 if err != nil { 428 return nil, model.NewAppError("SqlPostStore.GetPost", "store.sql_post.get.app_error", nil, "root_id="+rootId+err.Error(), http.StatusInternalServerError) 429 } 430 431 for _, p := range posts { 432 pl.AddPost(p) 433 pl.AddOrder(p.Id) 434 } 435 } 436 return pl, nil 437 } 438 439 func (s *SqlPostStore) GetSingle(id string) (*model.Post, *model.AppError) { 440 var post model.Post 441 err := s.GetReplica().SelectOne(&post, "SELECT * FROM Posts WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": id}) 442 if err != nil { 443 return nil, model.NewAppError("SqlPostStore.GetSingle", "store.sql_post.get.app_error", nil, "id="+id+err.Error(), http.StatusNotFound) 444 } 445 return &post, nil 446 } 447 448 type etagPosts struct { 449 Id string 450 UpdateAt int64 451 } 452 453 func (s *SqlPostStore) InvalidateLastPostTimeCache(channelId string) { 454 } 455 456 func (s *SqlPostStore) GetEtag(channelId string, allowFromCache bool) string { 457 var et etagPosts 458 err := s.GetReplica().SelectOne(&et, "SELECT Id, UpdateAt FROM Posts WHERE ChannelId = :ChannelId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"ChannelId": channelId}) 459 var result string 460 if err != nil { 461 result = fmt.Sprintf("%v.%v", model.CurrentVersion, model.GetMillis()) 462 } else { 463 result = fmt.Sprintf("%v.%v", model.CurrentVersion, et.UpdateAt) 464 } 465 466 return result 467 } 468 469 func (s *SqlPostStore) Delete(postId string, time int64, deleteByID string) *model.AppError { 470 471 appErr := func(errMsg string) *model.AppError { 472 return model.NewAppError("SqlPostStore.Delete", "store.sql_post.delete.app_error", nil, "id="+postId+", err="+errMsg, http.StatusInternalServerError) 473 } 474 475 var post model.Post 476 err := s.GetReplica().SelectOne(&post, "SELECT * FROM Posts WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": postId}) 477 if err != nil { 478 return appErr(err.Error()) 479 } 480 481 post.AddProp(model.POST_PROPS_DELETE_BY, deleteByID) 482 483 _, err = s.GetMaster().Exec("UPDATE Posts SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt, Props = :Props WHERE Id = :Id OR RootId = :RootId", map[string]interface{}{"DeleteAt": time, "UpdateAt": time, "Id": postId, "RootId": postId, "Props": model.StringInterfaceToJson(post.GetProps())}) 484 if err != nil { 485 return appErr(err.Error()) 486 } 487 488 return nil 489 } 490 491 func (s *SqlPostStore) permanentDelete(postId string) *model.AppError { 492 _, err := s.GetMaster().Exec("DELETE FROM Posts WHERE Id = :Id OR RootId = :RootId", map[string]interface{}{"Id": postId, "RootId": postId}) 493 if err != nil { 494 return model.NewAppError("SqlPostStore.Delete", "store.sql_post.permanent_delete.app_error", nil, "id="+postId+", err="+err.Error(), http.StatusInternalServerError) 495 } 496 return nil 497 } 498 499 func (s *SqlPostStore) permanentDeleteAllCommentByUser(userId string) *model.AppError { 500 _, err := s.GetMaster().Exec("DELETE FROM Posts WHERE UserId = :UserId AND RootId != ''", map[string]interface{}{"UserId": userId}) 501 if err != nil { 502 return model.NewAppError("SqlPostStore.permanentDeleteAllCommentByUser", "store.sql_post.permanent_delete_all_comments_by_user.app_error", nil, "userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 503 } 504 return nil 505 } 506 507 func (s *SqlPostStore) PermanentDeleteByUser(userId string) *model.AppError { 508 // First attempt to delete all the comments for a user 509 if err := s.permanentDeleteAllCommentByUser(userId); err != nil { 510 return err 511 } 512 513 // Now attempt to delete all the root posts for a user. This will also 514 // delete all the comments for each post 515 found := true 516 count := 0 517 518 for found { 519 var ids []string 520 _, err := s.GetMaster().Select(&ids, "SELECT Id FROM Posts WHERE UserId = :UserId LIMIT 1000", map[string]interface{}{"UserId": userId}) 521 if err != nil { 522 return model.NewAppError("SqlPostStore.PermanentDeleteByUser.select", "store.sql_post.permanent_delete_by_user.app_error", nil, "userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 523 } 524 525 found = false 526 for _, id := range ids { 527 found = true 528 if err := s.permanentDelete(id); err != nil { 529 return err 530 } 531 } 532 533 // This is a fail safe, give up if more than 10k messages 534 count++ 535 if count >= 10 { 536 return model.NewAppError("SqlPostStore.PermanentDeleteByUser.toolarge", "store.sql_post.permanent_delete_by_user.too_many.app_error", nil, "userId="+userId, http.StatusInternalServerError) 537 } 538 } 539 540 return nil 541 } 542 543 func (s *SqlPostStore) PermanentDeleteByChannel(channelId string) *model.AppError { 544 if _, err := s.GetMaster().Exec("DELETE FROM Posts WHERE ChannelId = :ChannelId", map[string]interface{}{"ChannelId": channelId}); err != nil { 545 return model.NewAppError("SqlPostStore.PermanentDeleteByChannel", "store.sql_post.permanent_delete_by_channel.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 546 } 547 return nil 548 } 549 550 func (s *SqlPostStore) GetPosts(options model.GetPostsOptions, _ bool) (*model.PostList, *model.AppError) { 551 if options.PerPage > 1000 { 552 return nil, model.NewAppError("SqlPostStore.GetLinearPosts", "store.sql_post.get_posts.app_error", nil, "channelId="+options.ChannelId, http.StatusBadRequest) 553 } 554 offset := options.PerPage * options.Page 555 556 rpc := make(chan store.StoreResult, 1) 557 go func() { 558 posts, err := s.getRootPosts(options.ChannelId, offset, options.PerPage, options.SkipFetchThreads) 559 rpc <- store.StoreResult{Data: posts, Err: err} 560 close(rpc) 561 }() 562 cpc := make(chan store.StoreResult, 1) 563 go func() { 564 posts, err := s.getParentsPosts(options.ChannelId, offset, options.PerPage, options.SkipFetchThreads) 565 cpc <- store.StoreResult{Data: posts, Err: err} 566 close(cpc) 567 }() 568 569 var err *model.AppError 570 list := model.NewPostList() 571 572 rpr := <-rpc 573 if rpr.Err != nil { 574 return nil, rpr.Err 575 } 576 577 cpr := <-cpc 578 if cpr.Err != nil { 579 return nil, cpr.Err 580 } 581 582 posts := rpr.Data.([]*model.Post) 583 parents := cpr.Data.([]*model.Post) 584 585 for _, p := range posts { 586 list.AddPost(p) 587 list.AddOrder(p.Id) 588 } 589 590 for _, p := range parents { 591 list.AddPost(p) 592 } 593 594 list.MakeNonNil() 595 596 return list, err 597 } 598 599 func (s *SqlPostStore) GetPostsSince(options model.GetPostsSinceOptions, allowFromCache bool) (*model.PostList, *model.AppError) { 600 var posts []*model.Post 601 602 replyCountQuery1 := "" 603 replyCountQuery2 := "" 604 if options.SkipFetchThreads { 605 replyCountQuery1 = `, (SELECT COUNT(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p1.RootId = '' THEN p1.Id ELSE p1.RootId END) AND Posts.DeleteAt = 0) as ReplyCount` 606 replyCountQuery2 = `, (SELECT COUNT(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p2.RootId = '' THEN p2.Id ELSE p2.RootId END) AND Posts.DeleteAt = 0) as ReplyCount` 607 } 608 var query string 609 610 // union of IDs and then join to get full posts is faster in mysql 611 if s.DriverName() == model.DATABASE_DRIVER_MYSQL { 612 query = `SELECT *` + replyCountQuery1 + ` FROM Posts p1 JOIN ( 613 (SELECT 614 Id 615 FROM 616 Posts p2 617 WHERE 618 (UpdateAt > :Time 619 AND ChannelId = :ChannelId) 620 LIMIT 1000) 621 UNION 622 (SELECT 623 Id 624 FROM 625 Posts p3 626 WHERE 627 Id 628 IN 629 (SELECT * FROM (SELECT 630 RootId 631 FROM 632 Posts 633 WHERE 634 UpdateAt > :Time 635 AND ChannelId = :ChannelId 636 LIMIT 1000) temp_tab)) 637 ) j ON p1.Id = j.Id 638 ORDER BY CreateAt DESC` 639 } else if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 640 query = ` 641 (SELECT 642 *` + replyCountQuery1 + ` 643 FROM 644 Posts p1 645 WHERE 646 (UpdateAt > :Time 647 AND ChannelId = :ChannelId) 648 LIMIT 1000) 649 UNION 650 (SELECT 651 *` + replyCountQuery2 + ` 652 FROM 653 Posts p2 654 WHERE 655 Id 656 IN 657 (SELECT * FROM (SELECT 658 RootId 659 FROM 660 Posts 661 WHERE 662 UpdateAt > :Time 663 AND ChannelId = :ChannelId 664 LIMIT 1000) temp_tab)) 665 ORDER BY CreateAt DESC` 666 } 667 _, err := s.GetReplica().Select(&posts, query, map[string]interface{}{"ChannelId": options.ChannelId, "Time": options.Time}) 668 669 if err != nil { 670 return nil, model.NewAppError("SqlPostStore.GetPostsSince", "store.sql_post.get_posts_since.app_error", nil, "channelId="+options.ChannelId+err.Error(), http.StatusInternalServerError) 671 } 672 673 list := model.NewPostList() 674 675 for _, p := range posts { 676 list.AddPost(p) 677 if p.UpdateAt > options.Time { 678 list.AddOrder(p.Id) 679 } 680 } 681 682 return list, nil 683 } 684 685 func (s *SqlPostStore) GetPostsBefore(options model.GetPostsOptions) (*model.PostList, *model.AppError) { 686 return s.getPostsAround(true, options) 687 } 688 689 func (s *SqlPostStore) GetPostsAfter(options model.GetPostsOptions) (*model.PostList, *model.AppError) { 690 return s.getPostsAround(false, options) 691 } 692 693 func (s *SqlPostStore) getPostsAround(before bool, options model.GetPostsOptions) (*model.PostList, *model.AppError) { 694 if options.Page < 0 || options.PerPage < 0 { 695 return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get.app_error", nil, 696 fmt.Sprintf("Page=%d and PerPage=%d must be non-negative", options.Page, options.PerPage), http.StatusBadRequest) 697 } 698 699 offset := options.Page * options.PerPage 700 var posts, parents []*model.Post 701 702 var direction string 703 var sort string 704 if before { 705 direction = "<" 706 sort = "DESC" 707 } else { 708 direction = ">" 709 sort = "ASC" 710 } 711 replyCountSubQuery := s.getQueryBuilder().Select("COUNT(Posts.Id)").From("Posts").Where(sq.Expr("Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0")) 712 query := s.getQueryBuilder().Select("p.*") 713 query = query.Column(sq.Alias(replyCountSubQuery, "ReplyCount")) 714 query = query.From("Posts p"). 715 Where(sq.And{ 716 sq.Expr(`CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = ?)`, options.PostId), 717 sq.Eq{"ChannelId": options.ChannelId}, 718 sq.Eq{"DeleteAt": int(0)}, 719 }). 720 // Adding ChannelId and DeleteAt order columns 721 // to let mysql choose the "idx_posts_channel_id_delete_at_create_at" index always. 722 // See MM-24170. 723 OrderBy("ChannelId", "DeleteAt", "CreateAt "+sort). 724 Limit(uint64(options.PerPage)). 725 Offset(uint64(offset)) 726 727 queryString, args, err := query.ToSql() 728 if err != nil { 729 return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get.app_error", nil, "channelId="+options.ChannelId+err.Error(), http.StatusInternalServerError) 730 } 731 _, err = s.GetMaster().Select(&posts, queryString, args...) 732 if err != nil { 733 return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get.app_error", nil, "channelId="+options.ChannelId+err.Error(), http.StatusInternalServerError) 734 } 735 736 if len(posts) > 0 { 737 rootIds := []string{} 738 for _, post := range posts { 739 rootIds = append(rootIds, post.Id) 740 if post.RootId != "" { 741 rootIds = append(rootIds, post.RootId) 742 } 743 } 744 rootQuery := s.getQueryBuilder().Select("p.*") 745 idQuery := sq.Or{ 746 sq.Eq{"Id": rootIds}, 747 } 748 rootQuery = rootQuery.Column(sq.Alias(replyCountSubQuery, "ReplyCount")) 749 if !options.SkipFetchThreads { 750 idQuery = append(idQuery, sq.Eq{"RootId": rootIds}) // preserve original behaviour 751 } 752 753 rootQuery = rootQuery.From("Posts p"). 754 Where(sq.And{ 755 idQuery, 756 sq.Eq{"ChannelId": options.ChannelId}, 757 sq.Eq{"DeleteAt": 0}, 758 }). 759 OrderBy("CreateAt DESC") 760 761 rootQueryString, rootArgs, err := rootQuery.ToSql() 762 763 if err != nil { 764 return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get_parent.app_error", nil, "channelId="+options.ChannelId+err.Error(), http.StatusInternalServerError) 765 } 766 _, err = s.GetMaster().Select(&parents, rootQueryString, rootArgs...) 767 if err != nil { 768 return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get_parent.app_error", nil, "channelId="+options.ChannelId+err.Error(), http.StatusInternalServerError) 769 } 770 } 771 772 list := model.NewPostList() 773 774 // We need to flip the order if we selected backwards 775 if before { 776 for _, p := range posts { 777 list.AddPost(p) 778 list.AddOrder(p.Id) 779 } 780 } else { 781 l := len(posts) 782 for i := range posts { 783 list.AddPost(posts[l-i-1]) 784 list.AddOrder(posts[l-i-1].Id) 785 } 786 } 787 788 for _, p := range parents { 789 list.AddPost(p) 790 } 791 792 return list, nil 793 } 794 795 func (s *SqlPostStore) GetPostIdBeforeTime(channelId string, time int64) (string, *model.AppError) { 796 return s.getPostIdAroundTime(channelId, time, true) 797 } 798 799 func (s *SqlPostStore) GetPostIdAfterTime(channelId string, time int64) (string, *model.AppError) { 800 return s.getPostIdAroundTime(channelId, time, false) 801 } 802 803 func (s *SqlPostStore) getPostIdAroundTime(channelId string, time int64, before bool) (string, *model.AppError) { 804 var direction sq.Sqlizer 805 var sort string 806 if before { 807 direction = sq.Lt{"CreateAt": time} 808 sort = "DESC" 809 } else { 810 direction = sq.Gt{"CreateAt": time} 811 sort = "ASC" 812 } 813 814 query := s.getQueryBuilder(). 815 Select("Id"). 816 From("Posts"). 817 Where(sq.And{ 818 direction, 819 sq.Eq{"ChannelId": channelId}, 820 sq.Eq{"DeleteAt": int(0)}, 821 }). 822 // Adding ChannelId and DeleteAt order columns 823 // to let mysql choose the "idx_posts_channel_id_delete_at_create_at" index always. 824 // See MM-23369. 825 OrderBy("ChannelId", "DeleteAt", "CreateAt "+sort). 826 Limit(1) 827 828 queryString, args, err := query.ToSql() 829 if err != nil { 830 return "", model.NewAppError("SqlPostStore.getPostIdAroundTime", "store.sql_post.get_post_id_around.app_error", nil, err.Error(), http.StatusInternalServerError) 831 } 832 833 var postId string 834 if err := s.GetMaster().SelectOne(&postId, queryString, args...); err != nil { 835 if err != sql.ErrNoRows { 836 return "", model.NewAppError("SqlPostStore.getPostIdAroundTime", "store.sql_post.get_post_id_around.app_error", nil, "channelId="+channelId+err.Error(), http.StatusInternalServerError) 837 } 838 } 839 840 return postId, nil 841 } 842 843 func (s *SqlPostStore) GetPostAfterTime(channelId string, time int64) (*model.Post, *model.AppError) { 844 query := s.getQueryBuilder(). 845 Select("*"). 846 From("Posts"). 847 Where(sq.And{ 848 sq.Gt{"CreateAt": time}, 849 sq.Eq{"ChannelId": channelId}, 850 sq.Eq{"DeleteAt": int(0)}, 851 }). 852 // Adding ChannelId and DeleteAt order columns 853 // to let mysql choose the "idx_posts_channel_id_delete_at_create_at" index always. 854 // See MM-23369. 855 OrderBy("ChannelId", "DeleteAt", "CreateAt ASC"). 856 Limit(1) 857 858 queryString, args, err := query.ToSql() 859 if err != nil { 860 return nil, model.NewAppError("SqlPostStore.GetPostAfterTime", "store.sql_post.get_post_after_time.app_error", nil, err.Error(), http.StatusInternalServerError) 861 } 862 863 var post *model.Post 864 if err := s.GetMaster().SelectOne(&post, queryString, args...); err != nil { 865 if err != sql.ErrNoRows { 866 return nil, model.NewAppError("SqlPostStore.GetPostAfterTime", "store.sql_post.get_post_after_time.app_error", nil, "channelId="+channelId+err.Error(), http.StatusInternalServerError) 867 } 868 } 869 870 return post, nil 871 } 872 873 func (s *SqlPostStore) getRootPosts(channelId string, offset int, limit int, skipFetchThreads bool) ([]*model.Post, *model.AppError) { 874 var posts []*model.Post 875 var fetchQuery string 876 if skipFetchThreads { 877 fetchQuery = "SELECT p.*, (SELECT COUNT(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount FROM Posts p WHERE ChannelId = :ChannelId AND DeleteAt = 0 ORDER BY CreateAt DESC LIMIT :Limit OFFSET :Offset" 878 } else { 879 fetchQuery = "SELECT * FROM Posts WHERE ChannelId = :ChannelId AND DeleteAt = 0 ORDER BY CreateAt DESC LIMIT :Limit OFFSET :Offset" 880 } 881 _, err := s.GetReplica().Select(&posts, fetchQuery, map[string]interface{}{"ChannelId": channelId, "Offset": offset, "Limit": limit}) 882 if err != nil { 883 return nil, model.NewAppError("SqlPostStore.GetLinearPosts", "store.sql_post.get_root_posts.app_error", nil, "channelId="+channelId+err.Error(), http.StatusInternalServerError) 884 } 885 return posts, nil 886 } 887 888 func (s *SqlPostStore) getParentsPosts(channelId string, offset int, limit int, skipFetchThreads bool) ([]*model.Post, *model.AppError) { 889 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 890 return s.getParentsPostsPostgreSQL(channelId, offset, limit, skipFetchThreads) 891 } 892 893 // query parent Ids first 894 var roots []*struct { 895 RootId string 896 } 897 rootQuery := ` 898 SELECT DISTINCT 899 q.RootId 900 FROM 901 (SELECT 902 RootId 903 FROM 904 Posts 905 WHERE 906 ChannelId = :ChannelId 907 AND DeleteAt = 0 908 ORDER BY CreateAt DESC 909 LIMIT :Limit OFFSET :Offset) q 910 WHERE q.RootId != ''` 911 912 _, err := s.GetReplica().Select(&roots, rootQuery, map[string]interface{}{"ChannelId": channelId, "Offset": offset, "Limit": limit}) 913 if err != nil { 914 return nil, model.NewAppError("SqlPostStore.GetLinearPosts", "store.sql_post.get_parents_posts.app_error", nil, "channelId="+channelId+" err="+err.Error(), http.StatusInternalServerError) 915 } 916 if len(roots) == 0 { 917 return nil, nil 918 } 919 params := make(map[string]interface{}) 920 placeholders := make([]string, len(roots)) 921 for idx, r := range roots { 922 key := fmt.Sprintf(":Root%v", idx) 923 params[key[1:]] = r.RootId 924 placeholders[idx] = key 925 } 926 placeholderString := strings.Join(placeholders, ", ") 927 params["ChannelId"] = channelId 928 replyCountQuery := "" 929 whereStatement := "p.Id IN (" + placeholderString + ")" 930 if skipFetchThreads { 931 replyCountQuery = `, (SELECT COUNT(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount` 932 } else { 933 whereStatement += " OR p.RootId IN (" + placeholderString + ")" 934 } 935 var posts []*model.Post 936 _, err = s.GetReplica().Select(&posts, ` 937 SELECT p.*`+replyCountQuery+` 938 FROM 939 Posts p 940 WHERE 941 (`+whereStatement+`) 942 AND ChannelId = :ChannelId 943 AND DeleteAt = 0 944 ORDER BY CreateAt`, 945 params) 946 if err != nil { 947 return nil, model.NewAppError("SqlPostStore.GetLinearPosts", "store.sql_post.get_parents_posts.app_error", nil, "channelId="+channelId+" err="+err.Error(), http.StatusInternalServerError) 948 } 949 return posts, nil 950 } 951 952 func (s *SqlPostStore) getParentsPostsPostgreSQL(channelId string, offset int, limit int, skipFetchThreads bool) ([]*model.Post, *model.AppError) { 953 var posts []*model.Post 954 replyCountQuery := "" 955 onStatement := "q1.RootId = q2.Id" 956 if skipFetchThreads { 957 replyCountQuery = ` ,(SELECT COUNT(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN q2.RootId = '' THEN q2.Id ELSE q2.RootId END) AND Posts.DeleteAt = 0) as ReplyCount` 958 } else { 959 onStatement += " OR q1.RootId = q2.RootId" 960 } 961 _, err := s.GetReplica().Select(&posts, 962 `SELECT q2.*`+replyCountQuery+` 963 FROM 964 Posts q2 965 INNER JOIN 966 (SELECT DISTINCT 967 q3.RootId 968 FROM 969 (SELECT 970 RootId 971 FROM 972 Posts 973 WHERE 974 ChannelId = :ChannelId1 975 AND DeleteAt = 0 976 ORDER BY CreateAt DESC 977 LIMIT :Limit OFFSET :Offset) q3 978 WHERE q3.RootId != '') q1 979 ON `+onStatement+` 980 WHERE 981 ChannelId = :ChannelId2 982 AND DeleteAt = 0 983 ORDER BY CreateAt`, 984 map[string]interface{}{"ChannelId1": channelId, "Offset": offset, "Limit": limit, "ChannelId2": channelId}) 985 if err != nil { 986 return nil, model.NewAppError("SqlPostStore.GetLinearPosts", "store.sql_post.get_parents_posts.app_error", nil, "channelId="+channelId+" err="+err.Error(), http.StatusInternalServerError) 987 } 988 return posts, nil 989 } 990 991 var specialSearchChar = []string{ 992 "<", 993 ">", 994 "+", 995 "-", 996 "(", 997 ")", 998 "~", 999 "@", 1000 ":", 1001 } 1002 1003 func (s *SqlPostStore) buildCreateDateFilterClause(params *model.SearchParams, queryParams map[string]interface{}) (string, map[string]interface{}) { 1004 searchQuery := "" 1005 // handle after: before: on: filters 1006 if len(params.OnDate) > 0 { 1007 onDateStart, onDateEnd := params.GetOnDateMillis() 1008 queryParams["OnDateStart"] = strconv.FormatInt(onDateStart, 10) 1009 queryParams["OnDateEnd"] = strconv.FormatInt(onDateEnd, 10) 1010 1011 // between `on date` start of day and end of day 1012 searchQuery += "AND CreateAt BETWEEN :OnDateStart AND :OnDateEnd " 1013 } else { 1014 1015 if len(params.ExcludedDate) > 0 { 1016 excludedDateStart, excludedDateEnd := params.GetExcludedDateMillis() 1017 queryParams["ExcludedDateStart"] = strconv.FormatInt(excludedDateStart, 10) 1018 queryParams["ExcludedDateEnd"] = strconv.FormatInt(excludedDateEnd, 10) 1019 1020 searchQuery += "AND CreateAt NOT BETWEEN :ExcludedDateStart AND :ExcludedDateEnd " 1021 } 1022 1023 if len(params.AfterDate) > 0 { 1024 afterDate := params.GetAfterDateMillis() 1025 queryParams["AfterDate"] = strconv.FormatInt(afterDate, 10) 1026 1027 // greater than `after date` 1028 searchQuery += "AND CreateAt >= :AfterDate " 1029 } 1030 1031 if len(params.BeforeDate) > 0 { 1032 beforeDate := params.GetBeforeDateMillis() 1033 queryParams["BeforeDate"] = strconv.FormatInt(beforeDate, 10) 1034 1035 // less than `before date` 1036 searchQuery += "AND CreateAt <= :BeforeDate " 1037 } 1038 1039 if len(params.ExcludedAfterDate) > 0 { 1040 afterDate := params.GetExcludedAfterDateMillis() 1041 queryParams["ExcludedAfterDate"] = strconv.FormatInt(afterDate, 10) 1042 1043 searchQuery += "AND CreateAt < :ExcludedAfterDate " 1044 } 1045 1046 if len(params.ExcludedBeforeDate) > 0 { 1047 beforeDate := params.GetExcludedBeforeDateMillis() 1048 queryParams["ExcludedBeforeDate"] = strconv.FormatInt(beforeDate, 10) 1049 1050 searchQuery += "AND CreateAt > :ExcludedBeforeDate " 1051 } 1052 } 1053 1054 return searchQuery, queryParams 1055 } 1056 1057 func (s *SqlPostStore) buildSearchChannelFilterClause(channels []string, paramPrefix string, exclusion bool, queryParams map[string]interface{}, byName bool) (string, map[string]interface{}) { 1058 if len(channels) == 0 { 1059 return "", queryParams 1060 } 1061 1062 clauseSlice := []string{} 1063 for i, channel := range channels { 1064 paramName := paramPrefix + strconv.FormatInt(int64(i), 10) 1065 clauseSlice = append(clauseSlice, ":"+paramName) 1066 queryParams[paramName] = channel 1067 } 1068 clause := strings.Join(clauseSlice, ", ") 1069 if byName { 1070 if exclusion { 1071 return "AND Name NOT IN (" + clause + ")", queryParams 1072 } 1073 return "AND Name IN (" + clause + ")", queryParams 1074 } 1075 1076 if exclusion { 1077 return "AND Id NOT IN (" + clause + ")", queryParams 1078 } 1079 return "AND Id IN (" + clause + ")", queryParams 1080 } 1081 1082 func (s *SqlPostStore) buildSearchUserFilterClause(users []string, paramPrefix string, exclusion bool, queryParams map[string]interface{}, byUsername bool) (string, map[string]interface{}) { 1083 if len(users) == 0 { 1084 return "", queryParams 1085 } 1086 clauseSlice := []string{} 1087 for i, user := range users { 1088 paramName := paramPrefix + strconv.FormatInt(int64(i), 10) 1089 clauseSlice = append(clauseSlice, ":"+paramName) 1090 queryParams[paramName] = user 1091 } 1092 clause := strings.Join(clauseSlice, ", ") 1093 if byUsername { 1094 if exclusion { 1095 return "AND Username NOT IN (" + clause + ")", queryParams 1096 } 1097 return "AND Username IN (" + clause + ")", queryParams 1098 } 1099 if exclusion { 1100 return "AND Id NOT IN (" + clause + ")", queryParams 1101 } 1102 return "AND Id IN (" + clause + ")", queryParams 1103 } 1104 1105 func (s *SqlPostStore) buildSearchPostFilterClause(fromUsers []string, excludedUsers []string, queryParams map[string]interface{}, userByUsername bool) (string, map[string]interface{}) { 1106 if len(fromUsers) == 0 && len(excludedUsers) == 0 { 1107 return "", queryParams 1108 } 1109 1110 filterQuery := ` 1111 AND UserId IN ( 1112 SELECT 1113 Id 1114 FROM 1115 Users, 1116 TeamMembers 1117 WHERE 1118 TeamMembers.TeamId = :TeamId 1119 AND Users.Id = TeamMembers.UserId 1120 FROM_USER_FILTER 1121 EXCLUDED_USER_FILTER)` 1122 1123 fromUserClause, queryParams := s.buildSearchUserFilterClause(fromUsers, "FromUser", false, queryParams, userByUsername) 1124 filterQuery = strings.Replace(filterQuery, "FROM_USER_FILTER", fromUserClause, 1) 1125 1126 excludedUserClause, queryParams := s.buildSearchUserFilterClause(excludedUsers, "ExcludedUser", true, queryParams, userByUsername) 1127 filterQuery = strings.Replace(filterQuery, "EXCLUDED_USER_FILTER", excludedUserClause, 1) 1128 1129 return filterQuery, queryParams 1130 } 1131 1132 func (s *SqlPostStore) Search(teamId string, userId string, params *model.SearchParams) (*model.PostList, *model.AppError) { 1133 return s.search(teamId, userId, params, true, true) 1134 } 1135 1136 func (s *SqlPostStore) search(teamId string, userId string, params *model.SearchParams, channelsByName bool, userByUsername bool) (*model.PostList, *model.AppError) { 1137 queryParams := map[string]interface{}{ 1138 "TeamId": teamId, 1139 "UserId": userId, 1140 } 1141 1142 list := model.NewPostList() 1143 if params.Terms == "" && params.ExcludedTerms == "" && 1144 len(params.InChannels) == 0 && len(params.ExcludedChannels) == 0 && 1145 len(params.FromUsers) == 0 && len(params.ExcludedUsers) == 0 && 1146 len(params.OnDate) == 0 && len(params.AfterDate) == 0 && len(params.BeforeDate) == 0 { 1147 return list, nil 1148 } 1149 1150 var posts []*model.Post 1151 1152 deletedQueryPart := "AND DeleteAt = 0" 1153 if params.IncludeDeletedChannels { 1154 deletedQueryPart = "" 1155 } 1156 1157 userIdPart := "AND UserId = :UserId" 1158 if params.SearchWithoutUserId { 1159 userIdPart = "" 1160 } 1161 1162 searchQuery := ` 1163 SELECT 1164 * ,(SELECT COUNT(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN q2.RootId = '' THEN q2.Id ELSE q2.RootId END) AND Posts.DeleteAt = 0) as ReplyCount 1165 FROM 1166 Posts q2 1167 WHERE 1168 DeleteAt = 0 1169 AND Type NOT LIKE '` + model.POST_SYSTEM_MESSAGE_PREFIX + `%' 1170 POST_FILTER 1171 AND ChannelId IN ( 1172 SELECT 1173 Id 1174 FROM 1175 Channels, 1176 ChannelMembers 1177 WHERE 1178 Id = ChannelId 1179 AND (TeamId = :TeamId OR TeamId = '') 1180 ` + userIdPart + ` 1181 ` + deletedQueryPart + ` 1182 IN_CHANNEL_FILTER 1183 EXCLUDED_CHANNEL_FILTER) 1184 CREATEDATE_CLAUSE 1185 SEARCH_CLAUSE 1186 ORDER BY CreateAt DESC 1187 LIMIT 100` 1188 1189 inChannelClause, queryParams := s.buildSearchChannelFilterClause(params.InChannels, "InChannel", false, queryParams, channelsByName) 1190 searchQuery = strings.Replace(searchQuery, "IN_CHANNEL_FILTER", inChannelClause, 1) 1191 1192 excludedChannelClause, queryParams := s.buildSearchChannelFilterClause(params.ExcludedChannels, "ExcludedChannel", true, queryParams, channelsByName) 1193 searchQuery = strings.Replace(searchQuery, "EXCLUDED_CHANNEL_FILTER", excludedChannelClause, 1) 1194 1195 postFilterClause, queryParams := s.buildSearchPostFilterClause(params.FromUsers, params.ExcludedUsers, queryParams, userByUsername) 1196 searchQuery = strings.Replace(searchQuery, "POST_FILTER", postFilterClause, 1) 1197 1198 createDateFilterClause, queryParams := s.buildCreateDateFilterClause(params, queryParams) 1199 searchQuery = strings.Replace(searchQuery, "CREATEDATE_CLAUSE", createDateFilterClause, 1) 1200 1201 termMap := map[string]bool{} 1202 terms := params.Terms 1203 excludedTerms := params.ExcludedTerms 1204 1205 searchType := "Message" 1206 if params.IsHashtag { 1207 searchType = "Hashtags" 1208 for _, term := range strings.Split(terms, " ") { 1209 termMap[strings.ToUpper(term)] = true 1210 } 1211 } 1212 1213 // these chars have special meaning and can be treated as spaces 1214 for _, c := range specialSearchChar { 1215 terms = strings.Replace(terms, c, " ", -1) 1216 excludedTerms = strings.Replace(excludedTerms, c, " ", -1) 1217 } 1218 1219 if terms == "" && excludedTerms == "" { 1220 // we've already confirmed that we have a channel or user to search for 1221 searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1) 1222 } else if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 1223 // Parse text for wildcards 1224 if wildcard, err := regexp.Compile(`\*($| )`); err == nil { 1225 terms = wildcard.ReplaceAllLiteralString(terms, ":* ") 1226 excludedTerms = wildcard.ReplaceAllLiteralString(excludedTerms, ":* ") 1227 } 1228 1229 excludeClause := "" 1230 if excludedTerms != "" { 1231 excludeClause = " & !(" + strings.Join(strings.Fields(excludedTerms), " | ") + ")" 1232 } 1233 1234 if params.OrTerms { 1235 queryParams["Terms"] = "(" + strings.Join(strings.Fields(terms), " | ") + ")" + excludeClause 1236 } else { 1237 queryParams["Terms"] = "(" + strings.Join(strings.Fields(terms), " & ") + ")" + excludeClause 1238 } 1239 1240 searchClause := fmt.Sprintf("AND to_tsvector('english', %s) @@ to_tsquery('english', :Terms)", searchType) 1241 searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", searchClause, 1) 1242 } else if s.DriverName() == model.DATABASE_DRIVER_MYSQL { 1243 searchClause := fmt.Sprintf("AND MATCH (%s) AGAINST (:Terms IN BOOLEAN MODE)", searchType) 1244 searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", searchClause, 1) 1245 1246 excludeClause := "" 1247 if excludedTerms != "" { 1248 excludeClause = " -(" + excludedTerms + ")" 1249 } 1250 1251 if params.OrTerms { 1252 queryParams["Terms"] = terms + excludeClause 1253 } else { 1254 splitTerms := []string{} 1255 for _, t := range strings.Fields(terms) { 1256 splitTerms = append(splitTerms, "+"+t) 1257 } 1258 queryParams["Terms"] = strings.Join(splitTerms, " ") + excludeClause 1259 } 1260 } 1261 1262 _, err := s.GetSearchReplica().Select(&posts, searchQuery, queryParams) 1263 if err != nil { 1264 mlog.Warn("Query error searching posts.", mlog.Err(err)) 1265 // Don't return the error to the caller as it is of no use to the user. Instead return an empty set of search results. 1266 } else { 1267 for _, p := range posts { 1268 if searchType == "Hashtags" { 1269 exactMatch := false 1270 for _, tag := range strings.Split(p.Hashtags, " ") { 1271 if termMap[strings.ToUpper(tag)] { 1272 exactMatch = true 1273 break 1274 } 1275 } 1276 if !exactMatch { 1277 continue 1278 } 1279 } 1280 list.AddPost(p) 1281 list.AddOrder(p.Id) 1282 } 1283 } 1284 list.MakeNonNil() 1285 return list, nil 1286 } 1287 1288 func (s *SqlPostStore) AnalyticsUserCountsWithPostsByDay(teamId string) (model.AnalyticsRows, *model.AppError) { 1289 query := 1290 `SELECT DISTINCT 1291 DATE(FROM_UNIXTIME(Posts.CreateAt / 1000)) AS Name, 1292 COUNT(DISTINCT Posts.UserId) AS Value 1293 FROM Posts` 1294 1295 if len(teamId) > 0 { 1296 query += " INNER JOIN Channels ON Posts.ChannelId = Channels.Id AND Channels.TeamId = :TeamId AND" 1297 } else { 1298 query += " WHERE" 1299 } 1300 1301 query += ` Posts.CreateAt >= :StartTime AND Posts.CreateAt <= :EndTime 1302 GROUP BY DATE(FROM_UNIXTIME(Posts.CreateAt / 1000)) 1303 ORDER BY Name DESC 1304 LIMIT 30` 1305 1306 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 1307 query = 1308 `SELECT 1309 TO_CHAR(DATE(TO_TIMESTAMP(Posts.CreateAt / 1000)), 'YYYY-MM-DD') AS Name, COUNT(DISTINCT Posts.UserId) AS Value 1310 FROM Posts` 1311 1312 if len(teamId) > 0 { 1313 query += " INNER JOIN Channels ON Posts.ChannelId = Channels.Id AND Channels.TeamId = :TeamId AND" 1314 } else { 1315 query += " WHERE" 1316 } 1317 1318 query += ` Posts.CreateAt >= :StartTime AND Posts.CreateAt <= :EndTime 1319 GROUP BY DATE(TO_TIMESTAMP(Posts.CreateAt / 1000)) 1320 ORDER BY Name DESC 1321 LIMIT 30` 1322 } 1323 1324 end := utils.MillisFromTime(utils.EndOfDay(utils.Yesterday())) 1325 start := utils.MillisFromTime(utils.StartOfDay(utils.Yesterday().AddDate(0, 0, -31))) 1326 1327 var rows model.AnalyticsRows 1328 _, err := s.GetReplica().Select( 1329 &rows, 1330 query, 1331 map[string]interface{}{"TeamId": teamId, "StartTime": start, "EndTime": end}) 1332 if err != nil { 1333 return nil, model.NewAppError("SqlPostStore.AnalyticsUserCountsWithPostsByDay", "store.sql_post.analytics_user_counts_posts_by_day.app_error", nil, err.Error(), http.StatusInternalServerError) 1334 } 1335 return rows, nil 1336 } 1337 1338 func (s *SqlPostStore) AnalyticsPostCountsByDay(options *model.AnalyticsPostCountsOptions) (model.AnalyticsRows, *model.AppError) { 1339 1340 query := 1341 `SELECT 1342 DATE(FROM_UNIXTIME(Posts.CreateAt / 1000)) AS Name, 1343 COUNT(Posts.Id) AS Value 1344 FROM Posts` 1345 1346 if options.BotsOnly { 1347 query += " INNER JOIN Bots ON Posts.UserId = Bots.Userid" 1348 } 1349 1350 if len(options.TeamId) > 0 { 1351 query += " INNER JOIN Channels ON Posts.ChannelId = Channels.Id AND Channels.TeamId = :TeamId AND" 1352 } else { 1353 query += " WHERE" 1354 } 1355 1356 query += ` Posts.CreateAt <= :EndTime 1357 AND Posts.CreateAt >= :StartTime 1358 GROUP BY DATE(FROM_UNIXTIME(Posts.CreateAt / 1000)) 1359 ORDER BY Name DESC 1360 LIMIT 30` 1361 1362 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 1363 query = 1364 `SELECT 1365 TO_CHAR(DATE(TO_TIMESTAMP(Posts.CreateAt / 1000)), 'YYYY-MM-DD') AS Name, Count(Posts.Id) AS Value 1366 FROM Posts` 1367 1368 if options.BotsOnly { 1369 query += " INNER JOIN Bots ON Posts.UserId = Bots.Userid" 1370 } 1371 1372 if len(options.TeamId) > 0 { 1373 query += " INNER JOIN Channels ON Posts.ChannelId = Channels.Id AND Channels.TeamId = :TeamId AND" 1374 } else { 1375 query += " WHERE" 1376 } 1377 1378 query += ` Posts.CreateAt <= :EndTime 1379 AND Posts.CreateAt >= :StartTime 1380 GROUP BY DATE(TO_TIMESTAMP(Posts.CreateAt / 1000)) 1381 ORDER BY Name DESC 1382 LIMIT 30` 1383 } 1384 1385 end := utils.MillisFromTime(utils.EndOfDay(utils.Yesterday())) 1386 start := utils.MillisFromTime(utils.StartOfDay(utils.Yesterday().AddDate(0, 0, -31))) 1387 if options.YesterdayOnly { 1388 start = utils.MillisFromTime(utils.StartOfDay(utils.Yesterday().AddDate(0, 0, -1))) 1389 } 1390 1391 var rows model.AnalyticsRows 1392 _, err := s.GetReplica().Select( 1393 &rows, 1394 query, 1395 map[string]interface{}{"TeamId": options.TeamId, "StartTime": start, "EndTime": end}) 1396 if err != nil { 1397 return nil, model.NewAppError("SqlPostStore.AnalyticsPostCountsByDay", "store.sql_post.analytics_posts_count_by_day.app_error", nil, err.Error(), http.StatusInternalServerError) 1398 } 1399 return rows, nil 1400 } 1401 1402 func (s *SqlPostStore) AnalyticsPostCount(teamId string, mustHaveFile bool, mustHaveHashtag bool) (int64, *model.AppError) { 1403 query := 1404 `SELECT 1405 COUNT(Posts.Id) AS Value 1406 FROM 1407 Posts, 1408 Channels 1409 WHERE 1410 Posts.ChannelId = Channels.Id` 1411 1412 if len(teamId) > 0 { 1413 query += " AND Channels.TeamId = :TeamId" 1414 } 1415 1416 if mustHaveFile { 1417 query += " AND (Posts.FileIds != '[]' OR Posts.Filenames != '[]')" 1418 } 1419 1420 if mustHaveHashtag { 1421 query += " AND Posts.Hashtags != ''" 1422 } 1423 1424 v, err := s.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId}) 1425 if err != nil { 1426 return 0, model.NewAppError("SqlPostStore.AnalyticsPostCount", "store.sql_post.analytics_posts_count.app_error", nil, err.Error(), http.StatusInternalServerError) 1427 } 1428 1429 return v, nil 1430 } 1431 1432 func (s *SqlPostStore) GetPostsCreatedAt(channelId string, time int64) ([]*model.Post, *model.AppError) { 1433 query := `SELECT * FROM Posts WHERE CreateAt = :CreateAt AND ChannelId = :ChannelId` 1434 1435 var posts []*model.Post 1436 _, err := s.GetReplica().Select(&posts, query, map[string]interface{}{"CreateAt": time, "ChannelId": channelId}) 1437 1438 if err != nil { 1439 return nil, model.NewAppError("SqlPostStore.GetPostsCreatedAt", "store.sql_post.get_posts_created_att.app_error", nil, "channelId="+channelId+err.Error(), http.StatusInternalServerError) 1440 } 1441 return posts, nil 1442 } 1443 1444 func (s *SqlPostStore) GetPostsByIds(postIds []string) ([]*model.Post, *model.AppError) { 1445 keys, params := MapStringsToQueryParams(postIds, "Post") 1446 1447 query := `SELECT p.*, (SELECT count(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount FROM Posts p WHERE p.Id IN ` + keys + ` ORDER BY CreateAt DESC` 1448 1449 var posts []*model.Post 1450 _, err := s.GetReplica().Select(&posts, query, params) 1451 1452 if err != nil { 1453 mlog.Error("Query error getting posts.", mlog.Err(err)) 1454 return nil, model.NewAppError("SqlPostStore.GetPostsByIds", "store.sql_post.get_posts_by_ids.app_error", nil, "", http.StatusInternalServerError) 1455 } 1456 return posts, nil 1457 } 1458 1459 func (s *SqlPostStore) GetPostsBatchForIndexing(startTime int64, endTime int64, limit int) ([]*model.PostForIndexing, *model.AppError) { 1460 var posts []*model.PostForIndexing 1461 _, err := s.GetSearchReplica().Select(&posts, 1462 `SELECT 1463 PostsQuery.*, Channels.TeamId, ParentPosts.CreateAt ParentCreateAt 1464 FROM ( 1465 SELECT 1466 * 1467 FROM 1468 Posts 1469 WHERE 1470 Posts.CreateAt >= :StartTime 1471 AND 1472 Posts.CreateAt < :EndTime 1473 ORDER BY 1474 CreateAt ASC 1475 LIMIT 1476 1000 1477 ) 1478 AS 1479 PostsQuery 1480 LEFT JOIN 1481 Channels 1482 ON 1483 PostsQuery.ChannelId = Channels.Id 1484 LEFT JOIN 1485 Posts ParentPosts 1486 ON 1487 PostsQuery.RootId = ParentPosts.Id`, 1488 map[string]interface{}{"StartTime": startTime, "EndTime": endTime, "NumPosts": limit}) 1489 1490 if err != nil { 1491 return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_batch_for_indexing.get.app_error", nil, err.Error(), http.StatusInternalServerError) 1492 } 1493 return posts, nil 1494 } 1495 1496 func (s *SqlPostStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, *model.AppError) { 1497 var query string 1498 if s.DriverName() == "postgres" { 1499 query = "DELETE from Posts WHERE Id = any (array (SELECT Id FROM Posts WHERE CreateAt < :EndTime LIMIT :Limit))" 1500 } else { 1501 query = "DELETE from Posts WHERE CreateAt < :EndTime LIMIT :Limit" 1502 } 1503 1504 sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"EndTime": endTime, "Limit": limit}) 1505 if err != nil { 1506 return 0, model.NewAppError("SqlPostStore.PermanentDeleteBatch", "store.sql_post.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 1507 } 1508 1509 rowsAffected, err := sqlResult.RowsAffected() 1510 if err != nil { 1511 return 0, model.NewAppError("SqlPostStore.PermanentDeleteBatch", "store.sql_post.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 1512 } 1513 return rowsAffected, nil 1514 } 1515 1516 func (s *SqlPostStore) GetOldest() (*model.Post, *model.AppError) { 1517 var post model.Post 1518 err := s.GetReplica().SelectOne(&post, "SELECT * FROM Posts ORDER BY CreateAt LIMIT 1") 1519 if err != nil { 1520 return nil, model.NewAppError("SqlPostStore.GetOldest", "store.sql_post.get.app_error", nil, err.Error(), http.StatusNotFound) 1521 } 1522 1523 return &post, nil 1524 } 1525 1526 func (s *SqlPostStore) determineMaxPostSize() int { 1527 var maxPostSizeBytes int32 1528 1529 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 1530 // The Post.Message column in Postgres has historically been VARCHAR(4000), but 1531 // may be manually enlarged to support longer posts. 1532 if err := s.GetReplica().SelectOne(&maxPostSizeBytes, ` 1533 SELECT 1534 COALESCE(character_maximum_length, 0) 1535 FROM 1536 information_schema.columns 1537 WHERE 1538 table_name = 'posts' 1539 AND column_name = 'message' 1540 `); err != nil { 1541 mlog.Error("Unable to determine the maximum supported post size", mlog.Err(err)) 1542 } 1543 } else if s.DriverName() == model.DATABASE_DRIVER_MYSQL { 1544 // The Post.Message column in MySQL has historically been TEXT, with a maximum 1545 // limit of 65535. 1546 if err := s.GetReplica().SelectOne(&maxPostSizeBytes, ` 1547 SELECT 1548 COALESCE(CHARACTER_MAXIMUM_LENGTH, 0) 1549 FROM 1550 INFORMATION_SCHEMA.COLUMNS 1551 WHERE 1552 table_schema = DATABASE() 1553 AND table_name = 'Posts' 1554 AND column_name = 'Message' 1555 LIMIT 0, 1 1556 `); err != nil { 1557 mlog.Error("Unable to determine the maximum supported post size", mlog.Err(err)) 1558 } 1559 } else { 1560 mlog.Warn("No implementation found to determine the maximum supported post size") 1561 } 1562 1563 // Assume a worst-case representation of four bytes per rune. 1564 maxPostSize := int(maxPostSizeBytes) / 4 1565 1566 // To maintain backwards compatibility, don't yield a maximum post 1567 // size smaller than the previous limit, even though it wasn't 1568 // actually possible to store 4000 runes in all cases. 1569 if maxPostSize < model.POST_MESSAGE_MAX_RUNES_V1 { 1570 maxPostSize = model.POST_MESSAGE_MAX_RUNES_V1 1571 } 1572 1573 mlog.Info("Post.Message has size restrictions", mlog.Int("max_characters", maxPostSize), mlog.Int32("max_bytes", maxPostSizeBytes)) 1574 1575 return maxPostSize 1576 } 1577 1578 // GetMaxPostSize returns the maximum number of runes that may be stored in a post. 1579 func (s *SqlPostStore) GetMaxPostSize() int { 1580 s.maxPostSizeOnce.Do(func() { 1581 s.maxPostSizeCached = s.determineMaxPostSize() 1582 }) 1583 return s.maxPostSizeCached 1584 } 1585 1586 func (s *SqlPostStore) GetParentsForExportAfter(limit int, afterId string) ([]*model.PostForExport, *model.AppError) { 1587 for { 1588 var rootIds []string 1589 _, err := s.GetReplica().Select(&rootIds, 1590 `SELECT 1591 Id 1592 FROM 1593 Posts 1594 WHERE 1595 Id > :AfterId 1596 AND RootId = '' 1597 AND DeleteAt = 0 1598 ORDER BY Id 1599 LIMIT :Limit`, 1600 map[string]interface{}{"Limit": limit, "AfterId": afterId}) 1601 if err != nil { 1602 return nil, model.NewAppError("SqlPostStore.GetAllAfterForExport", "store.sql_post.get_posts.app_error", 1603 nil, err.Error(), http.StatusInternalServerError) 1604 } 1605 1606 var postsForExport []*model.PostForExport 1607 if len(rootIds) == 0 { 1608 return postsForExport, nil 1609 } 1610 1611 keys, params := MapStringsToQueryParams(rootIds, "PostId") 1612 _, err = s.GetSearchReplica().Select(&postsForExport, ` 1613 SELECT 1614 p1.*, 1615 Users.Username as Username, 1616 Teams.Name as TeamName, 1617 Channels.Name as ChannelName 1618 FROM 1619 (Select * FROM Posts WHERE Id IN `+keys+`) p1 1620 INNER JOIN 1621 Channels ON p1.ChannelId = Channels.Id 1622 INNER JOIN 1623 Teams ON Channels.TeamId = Teams.Id 1624 INNER JOIN 1625 Users ON p1.UserId = Users.Id 1626 WHERE 1627 Channels.DeleteAt = 0 1628 AND Teams.DeleteAt = 0 1629 ORDER BY 1630 p1.Id`, 1631 params) 1632 if err != nil { 1633 return nil, model.NewAppError("SqlPostStore.GetAllAfterForExport", "store.sql_post.get_posts.app_error", 1634 nil, err.Error(), http.StatusInternalServerError) 1635 } 1636 1637 if len(postsForExport) == 0 { 1638 // All of the posts were in channels or teams that were deleted. 1639 // Update the afterId and try again. 1640 afterId = rootIds[len(rootIds)-1] 1641 continue 1642 } 1643 1644 return postsForExport, nil 1645 } 1646 } 1647 1648 func (s *SqlPostStore) GetRepliesForExport(rootId string) ([]*model.ReplyForExport, *model.AppError) { 1649 var posts []*model.ReplyForExport 1650 _, err := s.GetSearchReplica().Select(&posts, ` 1651 SELECT 1652 Posts.*, 1653 Users.Username as Username 1654 FROM 1655 Posts 1656 INNER JOIN 1657 Users ON Posts.UserId = Users.Id 1658 WHERE 1659 Posts.RootId = :RootId 1660 AND Posts.DeleteAt = 0 1661 ORDER BY 1662 Posts.Id`, 1663 map[string]interface{}{"RootId": rootId}) 1664 1665 if err != nil { 1666 return nil, model.NewAppError("SqlPostStore.GetAllAfterForExport", "store.sql_post.get_posts.app_error", nil, err.Error(), http.StatusInternalServerError) 1667 } 1668 1669 return posts, nil 1670 } 1671 1672 func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId string) ([]*model.DirectPostForExport, *model.AppError) { 1673 query := s.getQueryBuilder(). 1674 Select("p.*", "Users.Username as User"). 1675 From("Posts p"). 1676 Join("Channels ON p.ChannelId = Channels.Id"). 1677 Join("Users ON p.UserId = Users.Id"). 1678 Where(sq.And{ 1679 sq.Gt{"p.Id": afterId}, 1680 sq.Eq{"p.ParentId": string("")}, 1681 sq.Eq{"p.DeleteAt": int(0)}, 1682 sq.Eq{"Channels.DeleteAt": int(0)}, 1683 sq.Eq{"Users.DeleteAt": int(0)}, 1684 sq.Eq{"Channels.Type": []string{"D", "G"}}, 1685 }). 1686 OrderBy("p.Id"). 1687 Limit(uint64(limit)) 1688 1689 queryString, args, err := query.ToSql() 1690 if err != nil { 1691 return nil, model.NewAppError("SqlPostStore.GetDirectPostParentsForExportAfter", "store.sql_post.get_direct_posts.app_error", nil, err.Error(), http.StatusInternalServerError) 1692 } 1693 1694 var posts []*model.DirectPostForExport 1695 if _, err = s.GetReplica().Select(&posts, queryString, args...); err != nil { 1696 return nil, model.NewAppError("SqlPostStore.GetDirectPostParentsForExportAfter", "store.sql_post.get_direct_posts.app_error", nil, err.Error(), http.StatusInternalServerError) 1697 } 1698 var channelIds []string 1699 for _, post := range posts { 1700 channelIds = append(channelIds, post.ChannelId) 1701 } 1702 query = s.getQueryBuilder(). 1703 Select("u.Username as Username, ChannelId, UserId, cm.Roles as Roles, LastViewedAt, MsgCount, MentionCount, cm.NotifyProps as NotifyProps, LastUpdateAt, SchemeUser, SchemeAdmin, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest"). 1704 From("ChannelMembers cm"). 1705 Join("Users u ON ( u.Id = cm.UserId )"). 1706 Where(sq.Eq{ 1707 "cm.ChannelId": channelIds, 1708 }) 1709 1710 queryString, args, err = query.ToSql() 1711 if err != nil { 1712 return nil, model.NewAppError("SqlPostStore.GetDirectPostParentsForExportAfter", "store.sql_post.get_direct_posts.app_error", nil, err.Error(), http.StatusInternalServerError) 1713 } 1714 1715 var channelMembers []*model.ChannelMemberForExport 1716 if _, err := s.GetReplica().Select(&channelMembers, queryString, args...); err != nil { 1717 return nil, model.NewAppError("SqlPostStore.GetDirectPostParentsForExportAfter", "store.sql_post.get_direct_posts.app_error", nil, err.Error(), http.StatusInternalServerError) 1718 } 1719 1720 // Build a map of channels and their posts 1721 postsChannelMap := make(map[string][]*model.DirectPostForExport) 1722 for _, post := range posts { 1723 post.ChannelMembers = &[]string{} 1724 postsChannelMap[post.ChannelId] = append(postsChannelMap[post.ChannelId], post) 1725 } 1726 1727 // Build a map of channels and their members 1728 channelMembersMap := make(map[string][]string) 1729 for _, member := range channelMembers { 1730 channelMembersMap[member.ChannelId] = append(channelMembersMap[member.ChannelId], member.Username) 1731 } 1732 1733 // Populate each post ChannelMembers extracting it from the channelMembersMap 1734 for channelId := range channelMembersMap { 1735 for _, post := range postsChannelMap[channelId] { 1736 *post.ChannelMembers = channelMembersMap[channelId] 1737 } 1738 } 1739 return posts, nil 1740 } 1741 1742 func (s *SqlPostStore) SearchPostsInTeamForUser(paramsList []*model.SearchParams, userId, teamId string, isOrSearch, includeDeletedChannels bool, page, perPage int) (*model.PostSearchResults, *model.AppError) { 1743 // Since we don't support paging for DB search, we just return nothing for later pages 1744 if page > 0 { 1745 return model.MakePostSearchResults(model.NewPostList(), nil), nil 1746 } 1747 1748 var wg sync.WaitGroup 1749 1750 pchan := make(chan store.StoreResult, len(paramsList)) 1751 1752 for _, params := range paramsList { 1753 // remove any unquoted term that contains only non-alphanumeric chars 1754 // ex: abcd "**" && abc >> abcd "**" abc 1755 params.Terms = removeNonAlphaNumericUnquotedTerms(params.Terms, " ") 1756 params.IncludeDeletedChannels = includeDeletedChannels 1757 params.OrTerms = isOrSearch 1758 1759 wg.Add(1) 1760 1761 go func(params *model.SearchParams) { 1762 defer wg.Done() 1763 postList, err := s.search(teamId, userId, params, false, false) 1764 pchan <- store.StoreResult{Data: postList, Err: err} 1765 }(params) 1766 } 1767 1768 wg.Wait() 1769 close(pchan) 1770 1771 posts := model.NewPostList() 1772 1773 for result := range pchan { 1774 if result.Err != nil { 1775 return nil, result.Err 1776 } 1777 data := result.Data.(*model.PostList) 1778 posts.Extend(data) 1779 } 1780 1781 posts.SortByCreateAt() 1782 1783 return model.MakePostSearchResults(posts, nil), nil 1784 } 1785 1786 func (s *SqlPostStore) GetOldestEntityCreationTime() (int64, *model.AppError) { 1787 query := s.getQueryBuilder().Select("MIN(min_createat) min_createat"). 1788 Suffix(`FROM ( 1789 (SELECT MIN(createat) min_createat FROM Posts) 1790 UNION 1791 (SELECT MIN(createat) min_createat FROM Users) 1792 UNION 1793 (SELECT MIN(createat) min_createat FROM Channels) 1794 ) entities`) 1795 queryString, _, err := query.ToSql() 1796 if err != nil { 1797 return -1, model.NewAppError("SqlPostStore.GetOldestEntityCreationTime", 1798 "store.sql_post.get_oldest_entity_creation_time.app_error", nil, err.Error(), http.StatusInternalServerError) 1799 } 1800 row := s.GetReplica().Db.QueryRow(queryString) 1801 var oldest int64 1802 if err := row.Scan(&oldest); err != nil { 1803 return -1, model.NewAppError("SqlPostStore.GetOldestEntityCreationTime", 1804 "store.sql_post.get_oldest_entity_creation_time.app_error", nil, err.Error(), http.StatusInternalServerError) 1805 } 1806 return oldest, nil 1807 }