github.com/mad-app/mattermost-server@v5.11.1+incompatible/store/sqlstore/file_info_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 "net/http" 9 10 "github.com/mattermost/mattermost-server/einterfaces" 11 "github.com/mattermost/mattermost-server/model" 12 "github.com/mattermost/mattermost-server/store" 13 "github.com/mattermost/mattermost-server/utils" 14 ) 15 16 type SqlFileInfoStore struct { 17 SqlStore 18 metrics einterfaces.MetricsInterface 19 } 20 21 const ( 22 FILE_INFO_CACHE_SIZE = 25000 23 FILE_INFO_CACHE_SEC = 1800 // 30 minutes 24 ) 25 26 var fileInfoCache *utils.Cache = utils.NewLru(FILE_INFO_CACHE_SIZE) 27 28 func (fs SqlFileInfoStore) ClearCaches() { 29 fileInfoCache.Purge() 30 if fs.metrics != nil { 31 fs.metrics.IncrementMemCacheInvalidationCounter("File Info Cache - Purge") 32 } 33 } 34 35 func NewSqlFileInfoStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.FileInfoStore { 36 s := &SqlFileInfoStore{ 37 SqlStore: sqlStore, 38 metrics: metrics, 39 } 40 41 for _, db := range sqlStore.GetAllConns() { 42 table := db.AddTableWithName(model.FileInfo{}, "FileInfo").SetKeys(false, "Id") 43 table.ColMap("Id").SetMaxSize(26) 44 table.ColMap("CreatorId").SetMaxSize(26) 45 table.ColMap("PostId").SetMaxSize(26) 46 table.ColMap("Path").SetMaxSize(512) 47 table.ColMap("ThumbnailPath").SetMaxSize(512) 48 table.ColMap("PreviewPath").SetMaxSize(512) 49 table.ColMap("Name").SetMaxSize(256) 50 table.ColMap("Extension").SetMaxSize(64) 51 table.ColMap("MimeType").SetMaxSize(256) 52 } 53 54 return s 55 } 56 57 func (fs SqlFileInfoStore) CreateIndexesIfNotExists() { 58 fs.CreateIndexIfNotExists("idx_fileinfo_update_at", "FileInfo", "UpdateAt") 59 fs.CreateIndexIfNotExists("idx_fileinfo_create_at", "FileInfo", "CreateAt") 60 fs.CreateIndexIfNotExists("idx_fileinfo_delete_at", "FileInfo", "DeleteAt") 61 fs.CreateIndexIfNotExists("idx_fileinfo_postid_at", "FileInfo", "PostId") 62 } 63 64 func (fs SqlFileInfoStore) Save(info *model.FileInfo) store.StoreChannel { 65 return store.Do(func(result *store.StoreResult) { 66 info.PreSave() 67 if result.Err = info.IsValid(); result.Err != nil { 68 return 69 } 70 71 if err := fs.GetMaster().Insert(info); err != nil { 72 result.Err = model.NewAppError("SqlFileInfoStore.Save", "store.sql_file_info.save.app_error", nil, err.Error(), http.StatusInternalServerError) 73 } else { 74 result.Data = info 75 } 76 }) 77 } 78 79 func (fs SqlFileInfoStore) Get(id string) store.StoreChannel { 80 return store.Do(func(result *store.StoreResult) { 81 info := &model.FileInfo{} 82 83 if err := fs.GetReplica().SelectOne(info, 84 `SELECT 85 * 86 FROM 87 FileInfo 88 WHERE 89 Id = :Id 90 AND DeleteAt = 0`, map[string]interface{}{"Id": id}); err != nil { 91 if err == sql.ErrNoRows { 92 result.Err = model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusNotFound) 93 } else { 94 result.Err = model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusInternalServerError) 95 } 96 } else { 97 result.Data = info 98 } 99 }) 100 } 101 102 func (fs SqlFileInfoStore) GetByPath(path string) store.StoreChannel { 103 return store.Do(func(result *store.StoreResult) { 104 info := &model.FileInfo{} 105 106 if err := fs.GetReplica().SelectOne(info, 107 `SELECT 108 * 109 FROM 110 FileInfo 111 WHERE 112 Path = :Path 113 AND DeleteAt = 0 114 LIMIT 1`, map[string]interface{}{"Path": path}); err != nil { 115 result.Err = model.NewAppError("SqlFileInfoStore.GetByPath", "store.sql_file_info.get_by_path.app_error", nil, "path="+path+", "+err.Error(), http.StatusInternalServerError) 116 } else { 117 result.Data = info 118 } 119 }) 120 } 121 122 func (fs SqlFileInfoStore) InvalidateFileInfosForPostCache(postId string) { 123 fileInfoCache.Remove(postId) 124 if fs.metrics != nil { 125 fs.metrics.IncrementMemCacheInvalidationCounter("File Info Cache - Remove by PostId") 126 } 127 } 128 129 func (fs SqlFileInfoStore) GetForPost(postId string, readFromMaster bool, allowFromCache bool) store.StoreChannel { 130 return store.Do(func(result *store.StoreResult) { 131 if allowFromCache { 132 if cacheItem, ok := fileInfoCache.Get(postId); ok { 133 if fs.metrics != nil { 134 fs.metrics.IncrementMemCacheHitCounter("File Info Cache") 135 } 136 137 result.Data = cacheItem.([]*model.FileInfo) 138 return 139 } else { 140 if fs.metrics != nil { 141 fs.metrics.IncrementMemCacheMissCounter("File Info Cache") 142 } 143 } 144 } else { 145 if fs.metrics != nil { 146 fs.metrics.IncrementMemCacheMissCounter("File Info Cache") 147 } 148 } 149 150 var infos []*model.FileInfo 151 152 dbmap := fs.GetReplica() 153 154 if readFromMaster { 155 dbmap = fs.GetMaster() 156 } 157 158 if _, err := dbmap.Select(&infos, 159 `SELECT 160 * 161 FROM 162 FileInfo 163 WHERE 164 PostId = :PostId 165 AND DeleteAt = 0 166 ORDER BY 167 CreateAt`, map[string]interface{}{"PostId": postId}); err != nil { 168 result.Err = model.NewAppError("SqlFileInfoStore.GetForPost", 169 "store.sql_file_info.get_for_post.app_error", nil, "post_id="+postId+", "+err.Error(), http.StatusInternalServerError) 170 } else { 171 if len(infos) > 0 { 172 fileInfoCache.AddWithExpiresInSecs(postId, infos, FILE_INFO_CACHE_SEC) 173 } 174 175 result.Data = infos 176 } 177 }) 178 } 179 180 func (fs SqlFileInfoStore) GetForUser(userId string) store.StoreChannel { 181 return store.Do(func(result *store.StoreResult) { 182 var infos []*model.FileInfo 183 184 dbmap := fs.GetReplica() 185 186 if _, err := dbmap.Select(&infos, 187 `SELECT 188 * 189 FROM 190 FileInfo 191 WHERE 192 CreatorId = :CreatorId 193 AND DeleteAt = 0 194 ORDER BY 195 CreateAt`, map[string]interface{}{"CreatorId": userId}); err != nil { 196 result.Err = model.NewAppError("SqlFileInfoStore.GetForPost", 197 "store.sql_file_info.get_for_user_id.app_error", nil, "creator_id="+userId+", "+err.Error(), http.StatusInternalServerError) 198 } else { 199 result.Data = infos 200 } 201 }) 202 } 203 204 func (fs SqlFileInfoStore) AttachToPost(fileId, postId, creatorId string) store.StoreChannel { 205 return store.Do(func(result *store.StoreResult) { 206 sqlResult, err := fs.GetMaster().Exec( 207 `UPDATE 208 FileInfo 209 SET 210 PostId = :PostId 211 WHERE 212 Id = :Id 213 AND PostId = '' 214 AND CreatorId = :CreatorId`, map[string]interface{}{"PostId": postId, "Id": fileId, "CreatorId": creatorId}) 215 if err != nil { 216 result.Err = model.NewAppError("SqlFileInfoStore.AttachToPost", 217 "store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError) 218 return 219 } 220 221 count, err := sqlResult.RowsAffected() 222 if err != nil { 223 // RowsAffected should never fail with the MySQL or Postgres drivers 224 result.Err = model.NewAppError("SqlFileInfoStore.AttachToPost", 225 "store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError) 226 } else if count == 0 { 227 // Could not attach the file to the post 228 result.Err = model.NewAppError("SqlFileInfoStore.AttachToPost", 229 "store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId, http.StatusBadRequest) 230 } 231 }) 232 } 233 234 func (fs SqlFileInfoStore) DeleteForPost(postId string) store.StoreChannel { 235 return store.Do(func(result *store.StoreResult) { 236 if _, err := fs.GetMaster().Exec( 237 `UPDATE 238 FileInfo 239 SET 240 DeleteAt = :DeleteAt 241 WHERE 242 PostId = :PostId`, map[string]interface{}{"DeleteAt": model.GetMillis(), "PostId": postId}); err != nil { 243 result.Err = model.NewAppError("SqlFileInfoStore.DeleteForPost", 244 "store.sql_file_info.delete_for_post.app_error", nil, "post_id="+postId+", err="+err.Error(), http.StatusInternalServerError) 245 } else { 246 result.Data = postId 247 } 248 }) 249 } 250 251 func (fs SqlFileInfoStore) PermanentDelete(fileId string) store.StoreChannel { 252 return store.Do(func(result *store.StoreResult) { 253 if _, err := fs.GetMaster().Exec( 254 `DELETE FROM 255 FileInfo 256 WHERE 257 Id = :FileId`, map[string]interface{}{"FileId": fileId}); err != nil { 258 result.Err = model.NewAppError("SqlFileInfoStore.PermanentDelete", 259 "store.sql_file_info.permanent_delete.app_error", nil, "file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError) 260 } 261 }) 262 } 263 264 func (s SqlFileInfoStore) PermanentDeleteBatch(endTime int64, limit int64) store.StoreChannel { 265 return store.Do(func(result *store.StoreResult) { 266 var query string 267 if s.DriverName() == "postgres" { 268 query = "DELETE from FileInfo WHERE Id = any (array (SELECT Id FROM FileInfo WHERE CreateAt < :EndTime LIMIT :Limit))" 269 } else { 270 query = "DELETE from FileInfo WHERE CreateAt < :EndTime LIMIT :Limit" 271 } 272 273 sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"EndTime": endTime, "Limit": limit}) 274 if err != nil { 275 result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 276 } else { 277 rowsAffected, err1 := sqlResult.RowsAffected() 278 if err1 != nil { 279 result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 280 result.Data = int64(0) 281 } else { 282 result.Data = rowsAffected 283 } 284 } 285 }) 286 } 287 288 func (s SqlFileInfoStore) PermanentDeleteByUser(userId string) store.StoreChannel { 289 return store.Do(func(result *store.StoreResult) { 290 query := "DELETE from FileInfo WHERE CreatorId = :CreatorId" 291 292 sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"CreatorId": userId}) 293 if err != nil { 294 result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 295 } else { 296 rowsAffected, err1 := sqlResult.RowsAffected() 297 if err1 != nil { 298 result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 299 result.Data = int64(0) 300 } else { 301 result.Data = rowsAffected 302 } 303 } 304 }) 305 }