github.com/spline-fu/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  }