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  }