github.com/keys-pub/mattermost-server@v4.10.10+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) AttachToPost(fileId, postId string) store.StoreChannel { 181 return store.Do(func(result *store.StoreResult) { 182 if _, err := fs.GetMaster().Exec( 183 `UPDATE 184 FileInfo 185 SET 186 PostId = :PostId 187 WHERE 188 Id = :Id 189 AND PostId = ''`, map[string]interface{}{"PostId": postId, "Id": fileId}); err != nil { 190 result.Err = model.NewAppError("SqlFileInfoStore.AttachToPost", 191 "store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError) 192 } 193 }) 194 } 195 196 func (fs SqlFileInfoStore) DeleteForPost(postId string) store.StoreChannel { 197 return store.Do(func(result *store.StoreResult) { 198 if _, err := fs.GetMaster().Exec( 199 `UPDATE 200 FileInfo 201 SET 202 DeleteAt = :DeleteAt 203 WHERE 204 PostId = :PostId`, map[string]interface{}{"DeleteAt": model.GetMillis(), "PostId": postId}); err != nil { 205 result.Err = model.NewAppError("SqlFileInfoStore.DeleteForPost", 206 "store.sql_file_info.delete_for_post.app_error", nil, "post_id="+postId+", err="+err.Error(), http.StatusInternalServerError) 207 } else { 208 result.Data = postId 209 } 210 }) 211 } 212 213 func (fs SqlFileInfoStore) PermanentDelete(fileId string) store.StoreChannel { 214 return store.Do(func(result *store.StoreResult) { 215 if _, err := fs.GetMaster().Exec( 216 `DELETE FROM 217 FileInfo 218 WHERE 219 Id = :FileId`, map[string]interface{}{"FileId": fileId}); err != nil { 220 result.Err = model.NewAppError("SqlFileInfoStore.PermanentDelete", 221 "store.sql_file_info.permanent_delete.app_error", nil, "file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError) 222 } 223 }) 224 } 225 226 func (s SqlFileInfoStore) PermanentDeleteBatch(endTime int64, limit int64) store.StoreChannel { 227 return store.Do(func(result *store.StoreResult) { 228 var query string 229 if s.DriverName() == "postgres" { 230 query = "DELETE from FileInfo WHERE Id = any (array (SELECT Id FROM FileInfo WHERE CreateAt < :EndTime LIMIT :Limit))" 231 } else { 232 query = "DELETE from FileInfo WHERE CreateAt < :EndTime LIMIT :Limit" 233 } 234 235 sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"EndTime": endTime, "Limit": limit}) 236 if err != nil { 237 result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 238 } else { 239 rowsAffected, err1 := sqlResult.RowsAffected() 240 if err1 != nil { 241 result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 242 result.Data = int64(0) 243 } else { 244 result.Data = rowsAffected 245 } 246 } 247 }) 248 }