github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/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 "fmt" 9 "net/http" 10 11 sq "github.com/Masterminds/squirrel" 12 13 "github.com/vnforks/kid/v5/einterfaces" 14 "github.com/vnforks/kid/v5/model" 15 "github.com/vnforks/kid/v5/store" 16 ) 17 18 type SqlFileInfoStore struct { 19 SqlStore 20 metrics einterfaces.MetricsInterface 21 } 22 23 func (fs SqlFileInfoStore) ClearCaches() { 24 } 25 26 func newSqlFileInfoStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.FileInfoStore { 27 s := &SqlFileInfoStore{ 28 SqlStore: sqlStore, 29 metrics: metrics, 30 } 31 32 for _, db := range sqlStore.GetAllConns() { 33 table := db.AddTableWithName(model.FileInfo{}, "FileInfo").SetKeys(false, "Id") 34 table.ColMap("Id").SetMaxSize(26) 35 table.ColMap("CreatorId").SetMaxSize(26) 36 table.ColMap("PostId").SetMaxSize(26) 37 table.ColMap("Path").SetMaxSize(512) 38 table.ColMap("ThumbnailPath").SetMaxSize(512) 39 table.ColMap("PreviewPath").SetMaxSize(512) 40 table.ColMap("Name").SetMaxSize(256) 41 table.ColMap("Extension").SetMaxSize(64) 42 table.ColMap("MimeType").SetMaxSize(256) 43 } 44 45 return s 46 } 47 48 func (fs SqlFileInfoStore) createIndexesIfNotExists() { 49 fs.CreateIndexIfNotExists("idx_fileinfo_update_at", "FileInfo", "UpdateAt") 50 fs.CreateIndexIfNotExists("idx_fileinfo_create_at", "FileInfo", "CreateAt") 51 fs.CreateIndexIfNotExists("idx_fileinfo_delete_at", "FileInfo", "DeleteAt") 52 fs.CreateIndexIfNotExists("idx_fileinfo_postid_at", "FileInfo", "PostId") 53 } 54 55 func (fs SqlFileInfoStore) Save(info *model.FileInfo) (*model.FileInfo, *model.AppError) { 56 info.PreSave() 57 if err := info.IsValid(); err != nil { 58 return nil, err 59 } 60 61 if err := fs.GetMaster().Insert(info); err != nil { 62 return nil, model.NewAppError("SqlFileInfoStore.Save", "store.sql_file_info.save.app_error", nil, err.Error(), http.StatusInternalServerError) 63 } 64 return info, nil 65 } 66 67 func (fs SqlFileInfoStore) Get(id string) (*model.FileInfo, *model.AppError) { 68 info := &model.FileInfo{} 69 70 if err := fs.GetReplica().SelectOne(info, 71 `SELECT 72 * 73 FROM 74 FileInfo 75 WHERE 76 Id = :Id 77 AND DeleteAt = 0`, map[string]interface{}{"Id": id}); err != nil { 78 if err == sql.ErrNoRows { 79 return nil, model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusNotFound) 80 } 81 return nil, model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusInternalServerError) 82 } 83 return info, nil 84 } 85 86 func (fs SqlFileInfoStore) GetWithOptions(page, perPage int, opt *model.GetFileInfosOptions) ([]*model.FileInfo, *model.AppError) { 87 if perPage < 0 || page < 0 { 88 return nil, model.NewAppError("SqlFileInfoStore.GetWithOptions", 89 "store.sql_file_info.get_with_options.app_error", nil, fmt.Sprintf("page=%d and perPage=%d must be non-negative", page, perPage), http.StatusBadRequest) 90 } 91 if perPage == 0 { 92 return nil, nil 93 } 94 95 if opt == nil { 96 opt = &model.GetFileInfosOptions{} 97 } 98 99 query := fs.getQueryBuilder(). 100 Select("FileInfo.*"). 101 From("FileInfo") 102 103 if len(opt.ClassIds) > 0 { 104 query = query.Join("Posts ON FileInfo.PostId = Posts.Id"). 105 Where(sq.Eq{"Posts.ClassId": opt.ClassIds}) 106 } 107 108 if len(opt.UserIds) > 0 { 109 query = query.Where(sq.Eq{"FileInfo.CreatorId": opt.UserIds}) 110 } 111 112 if opt.Since > 0 { 113 query = query.Where(sq.GtOrEq{"FileInfo.CreateAt": opt.Since}) 114 } 115 116 if !opt.IncludeDeleted { 117 query = query.Where("FileInfo.DeleteAt = 0") 118 } 119 120 if opt.SortBy == "" { 121 opt.SortBy = model.FILEINFO_SORT_BY_CREATED 122 } 123 sortDirection := "ASC" 124 if opt.SortDescending { 125 sortDirection = "DESC" 126 } 127 128 switch opt.SortBy { 129 case model.FILEINFO_SORT_BY_CREATED: 130 query = query.OrderBy("FileInfo.CreateAt " + sortDirection) 131 case model.FILEINFO_SORT_BY_SIZE: 132 query = query.OrderBy("FileInfo.Size " + sortDirection) 133 default: 134 return nil, model.NewAppError("SqlFileInfoStore.GetWithOptions", 135 "store.sql_file_info.get_with_options.app_error", nil, "invalid sort option", http.StatusBadRequest) 136 } 137 138 query = query.OrderBy("FileInfo.Id ASC") // secondary sort for sort stability 139 140 query = query.Limit(uint64(perPage)).Offset(uint64(perPage * page)) 141 142 queryString, args, err := query.ToSql() 143 if err != nil { 144 return nil, model.NewAppError("SqlFileInfoStore.GetWithOptions", 145 "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError) 146 } 147 var infos []*model.FileInfo 148 if _, err := fs.GetReplica().Select(&infos, queryString, args...); err != nil { 149 return nil, model.NewAppError("SqlFileInfoStore.GetWithOptions", 150 "store.sql_file_info.get_with_options.app_error", nil, err.Error(), http.StatusInternalServerError) 151 } 152 return infos, nil 153 } 154 155 func (fs SqlFileInfoStore) GetByPath(path string) (*model.FileInfo, *model.AppError) { 156 info := &model.FileInfo{} 157 158 if err := fs.GetReplica().SelectOne(info, 159 `SELECT 160 * 161 FROM 162 FileInfo 163 WHERE 164 Path = :Path 165 AND DeleteAt = 0 166 LIMIT 1`, map[string]interface{}{"Path": path}); err != nil { 167 return nil, model.NewAppError("SqlFileInfoStore.GetByPath", "store.sql_file_info.get_by_path.app_error", nil, "path="+path+", "+err.Error(), http.StatusInternalServerError) 168 } 169 return info, nil 170 } 171 172 func (fs SqlFileInfoStore) InvalidateFileInfosForPostCache(postId string, deleted bool) { 173 } 174 175 func (fs SqlFileInfoStore) GetForPost(postId string, readFromMaster, includeDeleted, allowFromCache bool) ([]*model.FileInfo, *model.AppError) { 176 var infos []*model.FileInfo 177 178 dbmap := fs.GetReplica() 179 180 if readFromMaster { 181 dbmap = fs.GetMaster() 182 } 183 184 query := fs.getQueryBuilder(). 185 Select("*"). 186 From("FileInfo"). 187 Where(sq.Eq{"PostId": postId}). 188 OrderBy("CreateAt") 189 190 if !includeDeleted { 191 query = query.Where("DeleteAt = 0") 192 } 193 194 queryString, args, err := query.ToSql() 195 if err != nil { 196 return nil, model.NewAppError("SqlFileInfoStore.GetForPost", "store.sql_file_info.get_for_post.app_error", nil, err.Error(), http.StatusInternalServerError) 197 } 198 199 if _, err := dbmap.Select(&infos, queryString, args...); err != nil { 200 return nil, model.NewAppError("SqlFileInfoStore.GetForPost", 201 "store.sql_file_info.get_for_post.app_error", nil, "post_id="+postId+", "+err.Error(), http.StatusInternalServerError) 202 } 203 return infos, nil 204 } 205 206 func (fs SqlFileInfoStore) GetForUser(userId string) ([]*model.FileInfo, *model.AppError) { 207 var infos []*model.FileInfo 208 209 dbmap := fs.GetReplica() 210 211 if _, err := dbmap.Select(&infos, 212 `SELECT 213 * 214 FROM 215 FileInfo 216 WHERE 217 CreatorId = :CreatorId 218 AND DeleteAt = 0 219 ORDER BY 220 CreateAt`, map[string]interface{}{"CreatorId": userId}); err != nil { 221 return nil, model.NewAppError("SqlFileInfoStore.GetForUser", 222 "store.sql_file_info.get_for_user_id.app_error", nil, "creator_id="+userId+", "+err.Error(), http.StatusInternalServerError) 223 } 224 return infos, nil 225 } 226 227 func (fs SqlFileInfoStore) AttachToPost(fileId, postId, creatorId string) *model.AppError { 228 sqlResult, err := fs.GetMaster().Exec(` 229 UPDATE 230 FileInfo 231 SET 232 PostId = :PostId 233 WHERE 234 Id = :Id 235 AND PostId = '' 236 AND (CreatorId = :CreatorId OR CreatorId = 'nouser') 237 `, map[string]interface{}{ 238 "PostId": postId, 239 "Id": fileId, 240 "CreatorId": creatorId, 241 }) 242 if err != nil { 243 return model.NewAppError("SqlFileInfoStore.AttachToPost", 244 "store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError) 245 } 246 247 count, err := sqlResult.RowsAffected() 248 if err != nil { 249 // RowsAffected should never fail with the MySQL or Postgres drivers 250 return model.NewAppError("SqlFileInfoStore.AttachToPost", 251 "store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError) 252 } else if count == 0 { 253 // Could not attach the file to the post 254 return model.NewAppError("SqlFileInfoStore.AttachToPost", 255 "store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId, http.StatusBadRequest) 256 } 257 return nil 258 } 259 260 func (fs SqlFileInfoStore) DeleteForPost(postId string) (string, *model.AppError) { 261 if _, err := fs.GetMaster().Exec( 262 `UPDATE 263 FileInfo 264 SET 265 DeleteAt = :DeleteAt 266 WHERE 267 PostId = :PostId`, map[string]interface{}{"DeleteAt": model.GetMillis(), "PostId": postId}); err != nil { 268 return "", model.NewAppError("SqlFileInfoStore.DeleteForPost", 269 "store.sql_file_info.delete_for_post.app_error", nil, "post_id="+postId+", err="+err.Error(), http.StatusInternalServerError) 270 } 271 return postId, nil 272 } 273 274 func (fs SqlFileInfoStore) PermanentDelete(fileId string) *model.AppError { 275 if _, err := fs.GetMaster().Exec( 276 `DELETE FROM 277 FileInfo 278 WHERE 279 Id = :FileId`, map[string]interface{}{"FileId": fileId}); err != nil { 280 return model.NewAppError("SqlFileInfoStore.PermanentDelete", 281 "store.sql_file_info.permanent_delete.app_error", nil, "file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError) 282 } 283 return nil 284 } 285 286 func (fs SqlFileInfoStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, *model.AppError) { 287 var query string 288 if fs.DriverName() == "postgres" { 289 query = "DELETE from FileInfo WHERE Id = any (array (SELECT Id FROM FileInfo WHERE CreateAt < :EndTime LIMIT :Limit))" 290 } else { 291 query = "DELETE from FileInfo WHERE CreateAt < :EndTime LIMIT :Limit" 292 } 293 294 sqlResult, err := fs.GetMaster().Exec(query, map[string]interface{}{"EndTime": endTime, "Limit": limit}) 295 if err != nil { 296 return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 297 } 298 299 rowsAffected, err := sqlResult.RowsAffected() 300 if err != nil { 301 return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 302 } 303 304 return rowsAffected, nil 305 } 306 307 func (fs SqlFileInfoStore) PermanentDeleteByUser(userId string) (int64, *model.AppError) { 308 query := "DELETE from FileInfo WHERE CreatorId = :CreatorId" 309 310 sqlResult, err := fs.GetMaster().Exec(query, map[string]interface{}{"CreatorId": userId}) 311 if err != nil { 312 return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 313 } 314 315 rowsAffected, err := sqlResult.RowsAffected() 316 if err != nil { 317 return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 318 } 319 320 return rowsAffected, nil 321 }