github.com/wgh-/mattermost-server@v4.8.0-rc2+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 ClearFileCaches() { 29 fileInfoCache.Purge() 30 } 31 32 func NewSqlFileInfoStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.FileInfoStore { 33 s := &SqlFileInfoStore{ 34 SqlStore: sqlStore, 35 metrics: metrics, 36 } 37 38 for _, db := range sqlStore.GetAllConns() { 39 table := db.AddTableWithName(model.FileInfo{}, "FileInfo").SetKeys(false, "Id") 40 table.ColMap("Id").SetMaxSize(26) 41 table.ColMap("CreatorId").SetMaxSize(26) 42 table.ColMap("PostId").SetMaxSize(26) 43 table.ColMap("Path").SetMaxSize(512) 44 table.ColMap("ThumbnailPath").SetMaxSize(512) 45 table.ColMap("PreviewPath").SetMaxSize(512) 46 table.ColMap("Name").SetMaxSize(256) 47 table.ColMap("Extension").SetMaxSize(64) 48 table.ColMap("MimeType").SetMaxSize(256) 49 } 50 51 return s 52 } 53 54 func (fs SqlFileInfoStore) CreateIndexesIfNotExists() { 55 fs.CreateIndexIfNotExists("idx_fileinfo_update_at", "FileInfo", "UpdateAt") 56 fs.CreateIndexIfNotExists("idx_fileinfo_create_at", "FileInfo", "CreateAt") 57 fs.CreateIndexIfNotExists("idx_fileinfo_delete_at", "FileInfo", "DeleteAt") 58 fs.CreateIndexIfNotExists("idx_fileinfo_postid_at", "FileInfo", "PostId") 59 } 60 61 func (fs SqlFileInfoStore) Save(info *model.FileInfo) store.StoreChannel { 62 return store.Do(func(result *store.StoreResult) { 63 info.PreSave() 64 if result.Err = info.IsValid(); result.Err != nil { 65 return 66 } 67 68 if err := fs.GetMaster().Insert(info); err != nil { 69 result.Err = model.NewAppError("SqlFileInfoStore.Save", "store.sql_file_info.save.app_error", nil, err.Error(), http.StatusInternalServerError) 70 } else { 71 result.Data = info 72 } 73 }) 74 } 75 76 func (fs SqlFileInfoStore) Get(id string) store.StoreChannel { 77 return store.Do(func(result *store.StoreResult) { 78 info := &model.FileInfo{} 79 80 if err := fs.GetReplica().SelectOne(info, 81 `SELECT 82 * 83 FROM 84 FileInfo 85 WHERE 86 Id = :Id 87 AND DeleteAt = 0`, map[string]interface{}{"Id": id}); err != nil { 88 if err == sql.ErrNoRows { 89 result.Err = model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusNotFound) 90 } else { 91 result.Err = model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusInternalServerError) 92 } 93 } else { 94 result.Data = info 95 } 96 }) 97 } 98 99 func (fs SqlFileInfoStore) GetByPath(path string) store.StoreChannel { 100 return store.Do(func(result *store.StoreResult) { 101 info := &model.FileInfo{} 102 103 if err := fs.GetReplica().SelectOne(info, 104 `SELECT 105 * 106 FROM 107 FileInfo 108 WHERE 109 Path = :Path 110 AND DeleteAt = 0 111 LIMIT 1`, map[string]interface{}{"Path": path}); err != nil { 112 result.Err = model.NewAppError("SqlFileInfoStore.GetByPath", "store.sql_file_info.get_by_path.app_error", nil, "path="+path+", "+err.Error(), http.StatusInternalServerError) 113 } else { 114 result.Data = info 115 } 116 }) 117 } 118 119 func (fs SqlFileInfoStore) InvalidateFileInfosForPostCache(postId string) { 120 fileInfoCache.Remove(postId) 121 } 122 123 func (fs SqlFileInfoStore) GetForPost(postId string, readFromMaster bool, allowFromCache bool) store.StoreChannel { 124 return store.Do(func(result *store.StoreResult) { 125 if allowFromCache { 126 if cacheItem, ok := fileInfoCache.Get(postId); ok { 127 if fs.metrics != nil { 128 fs.metrics.IncrementMemCacheHitCounter("File Info Cache") 129 } 130 131 result.Data = cacheItem.([]*model.FileInfo) 132 return 133 } else { 134 if fs.metrics != nil { 135 fs.metrics.IncrementMemCacheMissCounter("File Info Cache") 136 } 137 } 138 } else { 139 if fs.metrics != nil { 140 fs.metrics.IncrementMemCacheMissCounter("File Info Cache") 141 } 142 } 143 144 var infos []*model.FileInfo 145 146 dbmap := fs.GetReplica() 147 148 if readFromMaster { 149 dbmap = fs.GetMaster() 150 } 151 152 if _, err := dbmap.Select(&infos, 153 `SELECT 154 * 155 FROM 156 FileInfo 157 WHERE 158 PostId = :PostId 159 AND DeleteAt = 0 160 ORDER BY 161 CreateAt`, map[string]interface{}{"PostId": postId}); err != nil { 162 result.Err = model.NewAppError("SqlFileInfoStore.GetForPost", 163 "store.sql_file_info.get_for_post.app_error", nil, "post_id="+postId+", "+err.Error(), http.StatusInternalServerError) 164 } else { 165 if len(infos) > 0 { 166 fileInfoCache.AddWithExpiresInSecs(postId, infos, FILE_INFO_CACHE_SEC) 167 } 168 169 result.Data = infos 170 } 171 }) 172 } 173 174 func (fs SqlFileInfoStore) AttachToPost(fileId, postId string) store.StoreChannel { 175 return store.Do(func(result *store.StoreResult) { 176 if _, err := fs.GetMaster().Exec( 177 `UPDATE 178 FileInfo 179 SET 180 PostId = :PostId 181 WHERE 182 Id = :Id 183 AND PostId = ''`, map[string]interface{}{"PostId": postId, "Id": fileId}); err != nil { 184 result.Err = model.NewAppError("SqlFileInfoStore.AttachToPost", 185 "store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError) 186 } 187 }) 188 } 189 190 func (fs SqlFileInfoStore) DeleteForPost(postId string) store.StoreChannel { 191 return store.Do(func(result *store.StoreResult) { 192 if _, err := fs.GetMaster().Exec( 193 `UPDATE 194 FileInfo 195 SET 196 DeleteAt = :DeleteAt 197 WHERE 198 PostId = :PostId`, map[string]interface{}{"DeleteAt": model.GetMillis(), "PostId": postId}); err != nil { 199 result.Err = model.NewAppError("SqlFileInfoStore.DeleteForPost", 200 "store.sql_file_info.delete_for_post.app_error", nil, "post_id="+postId+", err="+err.Error(), http.StatusInternalServerError) 201 } else { 202 result.Data = postId 203 } 204 }) 205 } 206 207 func (fs SqlFileInfoStore) PermanentDelete(fileId string) store.StoreChannel { 208 return store.Do(func(result *store.StoreResult) { 209 if _, err := fs.GetMaster().Exec( 210 `DELETE FROM 211 FileInfo 212 WHERE 213 Id = :FileId`, map[string]interface{}{"FileId": fileId}); err != nil { 214 result.Err = model.NewAppError("SqlFileInfoStore.PermanentDelete", 215 "store.sql_file_info.permanent_delete.app_error", nil, "file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError) 216 } 217 }) 218 } 219 220 func (s SqlFileInfoStore) PermanentDeleteBatch(endTime int64, limit int64) store.StoreChannel { 221 return store.Do(func(result *store.StoreResult) { 222 var query string 223 if s.DriverName() == "postgres" { 224 query = "DELETE from FileInfo WHERE Id = any (array (SELECT Id FROM FileInfo WHERE CreateAt < :EndTime LIMIT :Limit))" 225 } else { 226 query = "DELETE from FileInfo WHERE CreateAt < :EndTime LIMIT :Limit" 227 } 228 229 sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"EndTime": endTime, "Limit": limit}) 230 if err != nil { 231 result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 232 } else { 233 rowsAffected, err1 := sqlResult.RowsAffected() 234 if err1 != nil { 235 result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 236 result.Data = int64(0) 237 } else { 238 result.Data = rowsAffected 239 } 240 } 241 }) 242 }